swiftxctestxctestcasexctestexpectation

XCTest Async Function Swift


i'm quite new to XCTest, i have structured my code in Model, View, Controller

So controller will take data from Model and once got data, Controller will update View. So i have my Controller and view as follow

Controller:

func loadData() {
    Model.provideData { response in
        if response != nil {
            view.refresh()
        }
    }
}

View:

func refresh() {
    isViewLoaded = true
}

and here is my XCTest

func testLoadData() {
    let sut = Controller()
    let mockView = View()
    mockView.setController(controller: sut)
    controller.loadData()
    /** HERE is the problem, because it is a ASYNC call, i need to wait for the flag is set **/
    XCTAssertTrue(mockView.isViewLoaded, "isViewLoaded equals to true")
}

i know i can

let expectation = expectation(description: "wait for isViewLoaded set to true")

but where should i put the expectation.fulfill()?

waitForExpectation(timeout: 5, handler: nil)

Any help is appreciated. Thanks


Solution

  • You need your loadData to have a completion handler and hence be able to notify its callers when the async function is complete.

    func loadData(completion: @escaping () -> Void) {
        Model.provideData { response in
            if response != nil {
                view.refresh()
            }
            completion()
        }
    }
    

    And then in your test, do expectation.fulfill in the completion of loadData.

    func testLoadData() {
        let expectation = expectation(description: "wait for isViewLoaded set to true")
        let sut = Controller()
        let mockView = View()
        mockView.setController(controller: sut)
        controller.loadData {
            expectation.fulfill()
        }
        waitForExpectation(timeout: 5, handler: nil)
        XCTAssertTrue(mockView.isViewLoaded, "isViewLoaded equals to true")
    }