如何使用异步调用对@IBAction进行单元测试

时间:2015-12-13 17:43:37

标签: ios swift unit-testing xcode7 xctest

在下面的代码中,我想测试是否已调用“DisplayHelper.displayAlert”。我依赖注入DisplayHelperAuthService模拟对象,并使用PromiseKit

在我的ViewController中:

@IBAction func submitButtonPressed(sender: AnyObject) {
    AuthService.updateUser(["zipcode": self.zipcodeTextField.text!])
        .then { user -> Void in
             // sucess code
        }.error { error -> Void in
            self.DisplayHelper.displayAlert("Zipcode Not Found", message: "We can't find that zipcode... please try again.", callingViewController: self)
        }
}

以下是无效测试:

func testSubmitButtonServerCantFindZipcode() {
    vc.zipcodeTextField.text = "00000"
    vc.submitButtonPressed(self)

    // called AuthService    (passes)
    XCTAssertEqual(authServiceMock.updateUserCalled, true)

    // called displayAlert (fails because tests run before DisplayAlert is called)
    XCTAssertEqual(displayHelperMock.displayAlertCalled, true)
}

如何在断言之前让测试等待所有代码执行?

1 个答案:

答案 0 :(得分:2)

使用XCTest测试异步代码时,您需要使用XCTestExpectation

您可以像这样重写测试代码:

let e = expectationWithDescription("display alert")
waitForExpectationsWithTimeout(3) { error in
    XCTAssertEqual(displayHelperMock.displayAlertCalled, true)
}

现在,唯一缺少的测试工作就是找到一个可以调用expectation.fulfill()的地方。最合适的地方是AuthService模拟, 成功和失败回调后。

如果我可以建议你一些东西,编写测试是否已经调用了某些方法并不是一种安全的测试方法,因为你只是测试实现而不是行为。

更好的方法是独立测试两个组件。测试AuthService确保成功和失败路径都按预期执行,并DisplayHelper确保警报视图实际添加到视图层次结构中。

This article可能是一个有用的地方,可以开始了解单元测试警报的方式,this post可以很好地了解为什么以及如何避免模拟。