-
Notifications
You must be signed in to change notification settings - Fork 3
Unit Testing
TTOzzi edited this page Dec 20, 2020
·
3 revisions
- View์ ViewModel์ด Binding ๋์ด ์๋ ๊ตฌ์กฐ๋ก View์์ ๋ฐ์ํ ํน์ ์ด๋ฒคํธ๋ก ์ธํด ViewModel์ State ๊ฐ์ด ๋ฐ๋๋ฉด ๊ทธ ๊ฐ์ ์ํด View๊ฐ ์
๋ฐ์ดํธ ๋จ
- ๋ฐ๋ผ์, View๊ฐ ๊ทธ๋ ค์ง๋ ๋ก์ง ์ญ์ testing์ด ๊ฐ๋ฅํจ
- ๊ฐ์ฒด ๊ฐ ์์กด๊ด๊ณ๊ฐ ๋์์ง๋ฉด testing์ด ํ๋ค์ด์ง๋ฏ๋ก ์ถ์ํ๊ฐ ํ์ํจ. ํ์ฌ ํ๋ก์ ํธ์์ ์์กด๊ด๊ณ๋ฅผ ์ดํด๋ณด๋ฉด ViewModel - UseCase(+ EventLogger) - NetworkService ์ด๋ฏ๋ก,
- ViewModel์ test ํ๊ธฐ ์ํด์ UseCase๊ฐ ๋ฐ๋์ ํ์ํ๊ณ
- UseCase๋ฅผ test ํ๊ธฐ ์ํด์ NetworkService๊ฐ ๋ฐ๋์ ํ์ํจ.
- ๋ฐ๋ผ์ UseCase์ NetworkService๋ฅผ ์ถ์ํํ์ฌ Unit Test๋ฅผ ์งํํ ๋๋ ๋ชจ์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ฃผ์ ํ๋ ๋ฐฉ์์ผ๋ก ํ ์คํธ
- ๊ฐ๊ฐ์ ๊ฐ์ฒด์ ์ญํ ์ ๋ช ํํ๊ฒ ๋๋๊ณ , ์์กด์ฑ์ ๊ฐ์ง๋ ๊ฐ์ฒด๋ฅผ ํ๋กํ ์ฝ๋ก ์ถ์ํํ์ฌ ๋ชจ์ ๊ฐ์ฒด๋ฅผ ํตํด ๊ฐ์ฒด๊ฐ interaction์ ๋ํ ๋ ๋ฆฝ์ ์ธ ํ ์คํธ๊ฐ ๊ฐ๋ฅ
![](https://user-images.githubusercontent.com/34840140/102697849-6c94c880-427c-11eb-89d8-bdec4f7cfb1d.png)
- UseCaes๋ NetworkService์ ์์กด์ฑ์ ๊ฐ์ง๋ฏ๋ก ์ถ์ํ๋ NetworkServiceType์ ์ฑํํ์ฌ ๋ฌด์กฐ๊ฑด์ ์ผ๋ก ์ฑ๊ณต์ ์ธ response๋ฅผ ๋๋ ค์ฃผ๋ MockSuccessNetworkService ๊ตฌํ
struct MockSuccessfulNetworkService: NetworkServiceType {
let data: Data
func request(url: String, request type: RequestType, body: Data?) -> AnyPublisher<Data, NetworkError> {
return Just(data)
.setFailureType(to: NetworkError.self)
.eraseToAnyPublisher()
}
}
- UseCase๊ฐ '์ข์์' ์์ฒญ์ ๋คํธ์ํฌ๋ฅผ ํตํด ๋ณด๋ด๊ณ ๋์์จ ์๋ต์ด ์์ ํ๋ ์๋ต๊ณผ ์ผ์นํ๊ณ ์ต์ข ์ ์ผ๋ก ์คํจํ์ง ์๋ ๊ฒ์ด ๋ณด์ฅ๋์ด์ผ ํจ
func test_like_track_success() {
let expectations = XCTestExpectation(description: "track like success test")
defer { wait(for: [expectations], timeout: 5) }
let response = TrackUseCase.LikeTrackResponse(success: true)
let data = try? JSONEncoder().encode(likeTrack)
let usecase = TrackUseCase(network: MockSuccessNetworkService(data: data!))
usecase.likeTrack(id: 0)
.sink { result in
switch result {
case .finished:
expectations.fulfill() // ์ฑ๊ณตํ์ผ๋ฏ๋ก expectation fulfill
case let .failure(error):
XCTFail(error.localizedDescription) // ์ฑ๊ณตํด์ผ ๋ ์์ฒญ์ด ์คํจํ๋ค๋ฉด XCTFail
}
} receiveValue: { receivedResult in
XCTAssertEqual(receivedResult, response.success) // ๋ฐ์์จ ๊ฐ์ด ์์ํ๋ success = true์ ์ผ์นํด์ผ ํจ
}
.store(in: &cancellables)
}
- ViewModel์ UseCase์ EventLogger์ ์์กด์ฑ์ ๊ฐ์ง
- TrackUseCase๋ฅผ ์ถ์ํํ ํ๋กํ ์ฝ TrackuseCaseType์ ์ฑํํ์ฌ ์๋ํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐฉ์ถํด์ฃผ๋ MockTrackUseCase ๊ตฌํ
struct MockTrackuseCase: TrackuseCaseType {
let track: TrackInfo
...
func likeTrack(id: Int) -> AnyPublisher<Bool, UseCaseError> {
Just(true) // UseCase๋ฅผ ํตํ ์์ฒญ ๋ฐ ์๋ต์ด ์ฑ๊ณต์ ์ด์์
.setFailureType(to: UseCaseError.self)
.eraseToAnyPublisher()
}
}
- EventLogger๋ฅผ ์ถ์ํํ ํ๋กํ ์ฝ EventLoggerType์ ์ฑํํ์ฌ send๋ฉ์๋๊ฐ ์ ํธ์ถ๋์๋์ง ํ์ธํ ์ ์๋ MockEventLogger ๊ตฌํ
struct MockEventLogger: EventLoggerType {
var handler: ((EventLogType) -> Void)?
func send<T>(_ event: T) where T: EventLogType {
handler?(event)
}
}
- ViewModel์ State ๊ฐ์ด ๋ฐ๋ ์ ์๋๋ก View์์ ์ผ์ด๋ ์ด๋ฒคํธ๋ฅผ ๋ฃ์ด์ฃผ์ด ๊ฐ์ ๋ณํ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๋์๋์ง ๊ด์ฐฐํ๊ณ , ์ด๋ฒคํธ ๋ฐ์์ ๋ฐ๋ผ EventLogger๋ฅผ ํตํด ์ด๋ฒคํธ๋ฅผ ์ ์ ์กํ๋์ง๋ ํ ์คํธ
func test_like_track() {
let expectation = XCTestExpectation(description: "Like track test")
expectation.expectedFulfillmentCount = 2 // ์ด๋ฒคํธ๊ฐ ์ ๋๋ก ์ ์ก๋์๋์ง, State๊ฐ ์ ์์ ์ผ๋ก ๋ณ๊ฒฝ๋์๋์ง ํ์ธ์ ์ํด expectedFulfillmentCount๋ฅผ 2๋ก ์ค์
defer { wait(for: [expectation], timeout: 5) }
let usecase = MockTrackuseCase(track: track) // track.liked == false
let eventLogger = MockEventLogger { data in
XCTAssertEqual(data.event, "Like") // Like ์ด๋ฒคํธ๊ฐ ์ ๋๋ก ์ ์ก๋์๋์ง ํ์ธ
expectation.fulfill()
}
let viewModel = TrackViewModel(track: track, usecase: usecase, eventLogger: eventLogger)
viewModel.$state
.sink { state in
if state.track.liked { // track.liked == true ๋ก ๋ฐ๋์ด์ผ expectation fulfill
expectation.fulfill()
}
}
.store(in: &cancellables)
viewModel.send(.like) // View์์ ๋ฐ์ํ ์ข์์ ์ด๋ฒคํธ ์ ๋ฌ
}