Sign In
한결
Status
Empty
Assignee
Empty
Swift에서의 Unit Test의 기본2 - 비동기로 동작하는 코드의 테스트
기본적으로 XCTest는 동기적은 환경의 테스트를 지원한다.
그 이유는 모든 코드는 기본적으로 비동기적인 상황을 만들지 않은다면, 라인바이라인으로 동기적으로 동작하기 때문이다.
그래서, 비동기로 동작하는 코드를 테스트하기 위해서는 테스트 환경에서 비동기 테스크가 진행될 수 있는 환경을 먼저 만들어줘야 한다.
공식문서에 확인해보면 2가지 방법을 제시한다고 한다. (swift concurrency base, closure base)
swift concurrency 환경에서는 테스트 코드 자체를 async throws로 만들어서 내부 동작이 async하게 동작하다는 것을 인식시킬 수 있다.
// cc. apple developer swift documentation example code func testDownloadWebDataWithConcurrency() async throws { // Create a URL for a webpage to download. let url = URL(string: "https://apple.com")! // Use an asynchronous function to download the webpage. let dataAndResponse: (data: Data, response: URLResponse) = try await URLSession.shared.data(from: url, delegate: nil) // Assert that the actual response matches the expected response. let httpResponse = try XCTUnwrap(dataAndResponse.response as? HTTPURLResponse, "Expected an HTTPURLResponse.") XCTAssertEqual(httpResponse.statusCode, 200, "Expected a 200 OK response.") }
비동기 로직이 completionHandler와 같이 탈출 클로저 내부에서 동작한다면, XCTest가 제공하는 expectation 인스턴스, wait, fulfill 메서드를 통해 비동기 환경을 만들 수 있다.
func test_NetworkManager_FetchSuccess() { // given correctDrwNum = 1125 expectation = .init(description: "정확한 회차 정보에 대한 비동기 테스트") // when sut.fetchLotto(correctDrwNum) { [weak self] lotto, error in guard let self else { return } guard let lotto else { XCTAssertNil(lotto, "로또 정보가 없으면 nil 입니다.") expectation.fulfill() return } // then XCTAssertTrue(lotto.bnusNo >= 1, "정확한 회차를 반영하면 보너스 정보가 1~45 사의 숫자로 반환된다.") XCTAssertGreaterThanOrEqual(lotto.bnusNo, 1) // then XCTAssertTrue(lotto.bnusNo <= 45, "정확한 회차를 반영하면 보너스 정보가 1~45 사의 숫자로 반환된다.") XCTAssertLessThanOrEqual(lotto.bnusNo, 45) // end asynchronous expectation.fulfill() } wait(for: [expectation], timeout: 2.0) }
expectaion은 XCTestExpecation의 인스턴스이고, 비동기 작업의 시작과 끝남을 관장하는 역할을 한다.
wait 메서드는 expectaion 인스턴스가 비동기 환경을 얼마나 유지할 지를 결정해준다. timeout 설정으로 얼마나 기다릴지 정해줄 수 있다.
클로저 내부 구문에서는 expectation.fulfill() 호출 시점이 중요하다.
클로저의 탈출 조건을 충족하는 부분에서는 해당 비동기 작업 충족을 해주어야 하기 때문에 그 시점에 fulfill 메서드를 정확하게 호출시켜줘야 한다.
👍