지난 번에 Mocking에 대해서 포스팅을 하면서 해당 멘트를 적었던 것 같은데요. 이번 포스팅에도 이 원칙은 적용이 됩니다. 이번에는 Test를 전체 하나로 보지 말고 테스트 클래스 안에 있는 각각의 Test를 별개의 테스트로 보도록 합시다.
이 경우에도 마찬가지로 각각의 Test는 서로 독립적이어야 합니다. 하나의 Test가 실행되고 나서 다른 Test가 실행될 때 이전의 Test에서 세팅한 변수 혹은 실행한 메소드가 다음 Test에 영향을 미치면 안됩니다.
따라서 각각의 Test를 실행하기 전에 반드시 실행해야 하는 메소드를 만들어둡시다!
Quick과 Nimble을 사용해서 테스트를 작성하고 있습니다. 먼저 spec 함수 안에 Test의 대상이 되는 객체인 ViewModel과 해당 객체가 의존하고 있는 객체인 wordBookService의 변수를 구현해둡니다. !로 정의해두는 것은 실제 개발에서는 절대 지양해야할 방식이지만 Test에서는 일반적인 방식입니다. 혹시 해당 객체들이 세팅이 되어있지 않다면 테스트가 반드시 중단되어야 하기 때문입니다.
그리고 나서 prepare() 함수 내에서 해당 변수들에 새로운 인스턴스를 할당하면 됩니다. 이 함수를 테스트 전에 매번 실행하게 되면 완전히 fresh한 객체들로 테스트를 진행할 수 있으므로 각각의 테스트를 독립적으로 실시할 수 있게 됩니다.
class MacAddBookViewModelTest: QuickSpec {
override func spec() {
var viewModel: MacAddBookView.ViewModel!
var wordBookService: MockWordBookService!
func prepare() {
wordBookService = MockWordBookService()
viewModel = MacAddBookView.ViewModel(wordBookService: wordBookService)
}
}
Quick에서는 beforeEach를 사용해서 초기화 함수를 실행하면 좋습니다. beforeEach를 사용하면 해당 scope안에 있는 모든 Test를 실행하기 전에 후행 클로저를 매번 실행시켜줍니다.
describe("bookName") {
beforeEach {
prepare()
}
it("should be empty at first") {
expect(viewModel.bookName.isEmpty).to(beTrue())
}
context("when bookName has been updated with string") {
beforeEach {
viewModel.bookName = Random.string
}
context("when saveBook succeeded") {
it("should be emtpy") {
viewModel.saveBook()
expect(viewModel.bookName.isEmpty).toEventually(beTrue())
}
}
context("when saveBook failed") {
it("should be not be empty") {
wordBookService.saveBookError = AppError.generic(massage: "Mock Error")
viewModel.saveBook()
expect(viewModel.bookName.isEmpty).toEventually(beFalse())
}
}
}
}