我有以下单元测试:
let apiService = FreeApiService(httpClient: httpClient)
apiService.connect() {(status, error) in
XCTAssertTrue(status)
XCTAssertNil(error)
}
实际功能如下:
typealias freeConnectCompleteClosure = ( _ status: Bool, _ error: ApiServiceError?)->Void
class FreeApiService : FreeApiServiceProtocol {
func connect(complete: @escaping freeConnectCompleteClosure) {
...
case 200:
print("200 OK")
complete(true, nil)
}
}
这个单元测试正在通过,但问题是,如果我忘记了complete(true, nil)
部分,测试仍然会通过。如果没有调用闭包,我找不到默认情况下使测试失败的方法。
我错过了什么?
答案 0 :(得分:2)
您需要使用XCTTestExpectation
。除非明确履行,否则将无法通过测试。
通过调用expectation(description:)
或其中一个相关方法,将期望作为测试方法设置的一部分。在测试中的回调内部,根据期望调用fulfill()
。最后,在致电connect()
后,拨打XCTestCase
的“等待”方法之一,例如waitForExpectations(timeout:)
。如果您忘记在应用程序代码中调用回调,则超时将会过去,您将不会再出现误报。
Apple的"Testing Asynchronous Operations with Expectations"文档中提供了一个完整的示例。
答案 1 :(得分:1)
看起来您正在尝试针对FreeApiService编写测试。但是,从名称(和完成处理程序)判断,FreeApiService进行网络调用。这是单元测试中要避免的,因为测试不再仅仅依赖于您的代码。这取决于
如果您愿意忍受片状(由于上述依赖性)和缓慢(由于网络延迟)导致的测试,那么您可以编写异步测试。它看起来像这样:
func testConnect_ShouldCallCompletionHandlerWithTrueStatusAndNilError() {
let apiService = FreeApiService(httpClient: httpClient)
var capturedStatus: Bool?
var capturedError: Error?
let promise = expectation(description: "Completion handler invoked")
apiService.connect() {(status, error) in
capturedStatus = status
capturedError = error
promise.fulfill()
}
waitForExpectations(timeout: 5, handler: nil)
XCTAssertTrue(capturedStatus ?? false, "status")
XCTAssertNil(capturedError, "error")
}
如果未调用完成处理程序,waitForExpectations
将在5秒后超时。结果将导致测试失败。
我避免在完成处理程序中放置断言。相反,正如我在A Design Pattern for Tests that Do Real Networking中所描述的那样,我建议:
然后可以在完成处理程序之外执行所有断言。
...但是!我尝试为后端系统本身的验收测试保留异步测试。如果你想测试自己的代码,异步测试的混乱性质表明被测代码的设计需要改进。网络呼叫形成了明确的界限。我们可以测试到那个边界。而且我们可以测试任何被抛回我们身上的东西,但是我们可以在边界上进行测试。换句话说,我们可以测试:
重塑代码以允许这些测试可以让我们编写快速且确定的测试。他们甚至不需要互联网连接。