代码之家  ›  专栏  ›  技术社区  ›  Houman

如果未调用闭包,如何使XCTest失败?

  •  4
  • Houman  · 技术社区  · 6 年前

    我进行了以下单元测试:

    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) 部分,测试仍将通过。如果没有调用闭包,我无法找到使测试默认失败的方法。

    我错过了什么?

    2 回复  |  直到 6 年前
        1
  •  4
  •   Community rcollyer    4 年前

    您需要使用 XCTTestExpectation 为了这个。除非明确满足,否则测试将失败。

    通过调用 expectation(description:) 或相关方法之一。在测试的回调函数中,调用 fulfill() 在期望值上。最后,在您致电 connect() ,调用其中一个 XCTestCase 的“等待”方法,例如 waitForExpectations(timeout:) . 如果您忘记在应用程序代码中调用回调,则超时时间将过去,您将不再有误报。

    苹果的 "Testing Asynchronous Operations with Expectations" 文件编号:。

        2
  •  2
  •   Jon Reid    6 年前

    看起来您正在尝试编写针对FreeApiService的测试。然而,从名称(和完成处理程序)来看,FreeApiService会进行网络调用。这是在单元测试中要避免的,因为测试不再仅仅依赖于您的代码。这取决于

    • 具有可靠的Internet连接
    • 后端正在启动
    • 后端在给定超时之前响应
    • 发出预期响应的后端

    如果您愿意忍受脆弱(由于上面的依赖关系)和缓慢(由于网络延迟)的测试,那么您可以编写异步测试。它看起来像这样:

    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 ,我建议:

    • 捕获要测试的参数
    • 触发退出标志

    然后可以在完成处理程序之外执行所有断言。

    但是我尝试为后端系统本身的验收测试保留异步测试。如果您想测试自己的代码,异步测试的混乱性质表明,被测代码的设计需要改进。网络通话形成了明确的界限。我们可以测试到那个边界。我们可以测试任何被扔到我们身上的东西,但在我们这一边。换句话说,我们可以测试:

    • 网络请求的格式是否正确?但不要发出请求。
    • 我们是否正确处理网络响应?但要模拟反应。

    修改代码以允许这些测试将使我们能够编写快速且确定的测试。他们甚至不需要互联网连接。