iOS

Asynchronous Operations Unit Test (URLSession Unit Test)

JoonSwift 2021. 1. 27. 13:35

Unit Test를 공부하던 중에 평소 Model을 만들어 Mock 데이터를 활용해서 Model이 알맞게 만들어졌는지에 대한 Unit Test만 작성하다가, 

URLSession data task를 잘 가져오는지 확인하려고 Unit Test를 작성하던 도중 그냥 URLSession 만을 사용해서는 Unit Test 도중에 데이터를 잘 받아왔는지 확인하기도 전에 Unit Test가 Success했다고 나오는 현상을 발견했습니다.

 

그래서 이번 포스팅에서는 비동기 작업을 Testing하는 방법에 대해 알아보겠습니다. 

우선 저는 애플의 공식 문서인 'Testing Asynchronous Operations with Expectations' 라는 Article을 참고하여 공부를 해 보았습니다.

 

XCTestExpectation

비동기 작업에서 테스팅을 하기 위해서는 XCTestExpectation 인스턴스를 생성하여 사용해야 합니다. 이 Expectation이 URLSession data task의 작업이 Fulfill 될 때 까지 기다리는 형식입니다. 

정리하자면

  • XCTestExpectation 인스턴스를 생성한다.
  • 어떤 비동기 작업이 끝나는 completion handler 에서 작업이 끝났다는 것을 알려주기 위해 Expectation의 fulfill()메서드를 호출해 줍니다.
  • 작업에 대한 Timeout을 주기 위해 wait(for:timeout:)메서드를 생성하여 어느정도의 시간이 지나도 작업이 완료되지 않으면 실패했다는 것을 알려줄 수 있게 합니다. 

제가 연습하던 코드를 한번 보겠습니다. 

 func test_fetch_crypto_data() {
        let expectation = XCTestExpectation(description: "Fetch Crypto Data")

crypto data를 받아오는 Unit Test 메서드 입니다. 우선 XCTestExpectation 인스턴스를 생성해 줍니다. 

 

다음에는 바로 URLSession의 dataTask로 가보겠습니다. 

	URLSession.shared.dataTask(with: request) { data, response, error in
            if error != nil {
                XCTFail("Error Occured!")
                return
            }
            guard let response = response as? HTTPURLResponse,
                  (200...299).contains(response.statusCode) else {
                XCTFail("Can't get response status code between 200 ~ 299")
                return
            }
            guard let data = data else {
                XCTFail("Can't get right Data")
                return
            }
            
            do {
                let json = try JSONDecoder().decode(CurrencyData.self, from: data)
                XCTAssertEqual(json.cryptoData?[0].name, "Bitcoin")
            } catch {
                XCTFail("Decoding Error occur!")
            }
            expectation.fulfill()
        }.resume()

dataTask메서드가 완료되는 시점은 바로 completion handler가 실행되는 시점입니다. 다양한 에러에 대한 체크를 하고 난 후 그 결과에 대한 정보가 왔다는 것을 expectation.fullfill()  를 호출해주면서 알려줍니다. 

 

wait(for: [expectation], timeout: 10.0)

마지막으로 expectation에 대한 Timeout을 걸어줍니다. 10초를 설정하여 만약 10초가 지나도 expectation에 대한 응답이 없다면 이 테스트는 실패했다고 볼 수 있게 됩니다. 

 

그렇다면 실패할 수 있는 방법에는 간단하게 2가지로 확인해 볼 수 있습니다.

  • completion handler가 data에게 nil 값을 할당하여 XCTAssertNotNil 메서드에 걸려서 실패하는 경우
  • 설정한 timeout 보다 긴 시간이 걸리는 작업이거나, 네트워크 환경이 좋지 않아서 timeout 시간을 초과하는 경우 expectation이 fulfill()을 호출하지 못해서 실패하는 경우