XCTAssert의 비동기 Assertion (XCTAssertEventually) 만들어 보았습니다.

9075 단어 XcodeSwiftTDDXCTest

소개



안녕하세요.
갑작스럽지만 iOS의 유닛 테스트에서 XCTest를 사용하여 비동기 테스트를 작성하는 것은 상당히 귀찮습니다.
최근 여러가지로 Quick/Nimble에서 XCTest로 되돌렸습니다만, Nimble에 있던 toEventually가 상당히 매력으로, XCTest에서 XCTExpectation이나 wait(for: [expectation], timeout: 1.0)라든지 매번 쓰는 귀찮다고 생각해 있었습니다.
그래서 XCTest에서도 Nimble의 toEventually와 같이 간단하게 비동기의 Assertion이 쓸 수 있는 Extension을 만들어 보았습니다.

XCTestExtensions
htps : // 기주 b. 코 m / 신주 / XC에서 x x 신시온 s

사용법



비동기 처리의 값을 확인하는 예입니다.
    func test_XCTAssertTrueEventually() {
        var value = false

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
            value = true
        }

        XCTAssertTrueEventually(value) // success
    }

UI 관련 테스트에 적용한 예.
(ViewControllerA 버튼을 탭하면 ViewControllerB가 표시됩니다)
    func test_tappedButton() {
        var viewControllerA = ViewControllerA()

        viewControllerA.button.sendActions(for: .touchUpInside)

        XCTAssertTrueEventually(viewControllerA.presentedViewController is ViewControllerB) // success
    }

구현


    public func XCTAssertTrueEventually(_ expression: @escaping @autoclosure () throws -> Bool, timeout: TimeInterval = 1.0, pollInterval: TimeInterval = 0.1, file: StaticString = #file, line: UInt = #line) {
        let expectation = XCTestExpectation()
        for i in 0..<Int(timeout/pollInterval) {
            DispatchQueue.main.asyncAfter(deadline: .now() + pollInterval * Double(i)) {
                if (try! expression()) {
                    expectation.fulfill()
                }
            }
        }

        switchProcess(
            by: XCTWaiter.wait(for: [expectation], timeout: timeout),
            timedOutMessage: "XCTAssertNotNilEventually failed",
            file: file,
            line: line
        )
    }

    private func switchProcess(by result: XCTWaiter.Result, timedOutMessage: String, file: StaticString = #file, line: UInt = #line) {
        switch result {
        case .completed:
            break
        case .timedOut:
            XCTFail(timedOutMessage, file: file, line: line)
        default:
            XCTFail("resultに応じたmessageをいれると良い", file: file, line: line)
        }
    }

간단한 해설



구현 자체는 XCTestExpectation과 XCTWaiter를 사용한 비동기 처리에 DispatchQueue의 asyncAfter를 결합한 것입니다.
timeout과 pollInterval을 바탕으로 일정 간격으로 expression을 실행한 결과에서 fulfill()이 실행되어 XCTWaiter.wait에서 얻을 수 있는 값으로부터 최종적인 처리가 정해지게 되어 있습니다.
timeout과 pollInterval은 호출시 설정할 수 있으므로 테스트별로 조정할 수도 있습니다.

인터페이스는 XCTAssert를 참고하면서 기존 메서드를 사용할 때 알 수 있도록 끝에 Eventually를 붙이게 했습니다.


단지 wrapper라고 하면 그렇습니다만, 사용할 때 매번 작법과 같이 XCTestExpectation등의 코드를 쓰지 않아도 좋아지기 때문에 마음에 듭니다.

마지막으로



덧붙여서 이번에 쓴 코드는, 회사에서 「평소의 안건 업무와 직접 관계없는 일 합시다~」라고 하는 날이 있었으므로 생각나서 쓴 코드입니다.
그런 다음 몇 가지 종류의 Assertion을 함께 OSS로 공개했습니다.
좋으면 사용해보고 스타와 PullRequest를 기다리고 있습니다 mm

좋은 웹페이지 즐겨찾기