【问题标题】:How To Unit Test an @IBAction with asyncronous call如何使用异步调用对@IBAction 进行单元测试
【发布时间】:2016-03-19 04:14:44
【问题描述】:

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

在我的视图控制器中:

@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)
}

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

【问题讨论】:

    标签: ios swift unit-testing xcode7 xctest


    【解决方案1】:

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

    你可以这样重写测试代码:

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

    现在,让测试工作唯一缺少的部分是找到一个可以致电expectation.fulfill() 的地方。最合适的位置是在您的 AuthService 模拟中,成功和失败回调运行之后。

    不过,如果我可以建议您的话,编写测试来断言某些方法是否已被调用并不是一种安全的测试方法,因为您只是在测试实现而不是行为。

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

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

    【讨论】:

    • 感谢您查看此内容...听起来是一个不错的起点,但我很犹豫是否将期望嵌入到模拟中,因为它需要了解该方法的内部工作原理——@ 987654330@ 必须放在最后一次异步调用之后,因此添加另一个异步方法或更改调用顺序会破坏测试。有什么方法可以等待所有测试代码执行吗?再次感谢!
    猜你喜欢
    • 2020-12-22
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 2013-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-21
    相关资源
    最近更新 更多