首页 > 解决方案 > 在从 XCTestCase 调用的代码中使用 Dispatch.main 不起作用

问题描述

我有一个函数是围绕同步函数的异步包装器。

同步函数如下:

class Foo {
    class func bar() -> [Int] {
        return [1,2,3]
    }

    class func asyncBar(completion: @escaping ([Int]) -> Void) {
        DispatchQueue.global(qos: .userInitiated).async {
            let intArray = bar()
            DispatchQueue.main.async {
                completion(intArray)
            }
        }
    }
}

XCTestCase completion当我从不运行时调用它。

在 XCode 中完成单元测试的方式和主线程之间是否存在某种不正当的交互?

我在网上找不到关于此的文档。

我必须使用主线程进行回调,因为它与 Gui 交互。

我的测试用例看起来像:

func testAsyncBar() throws {
    var run = true
    func stopThisThing(ints: [Int]) {
        run = false
    }
    Foo.asyncBar(completion: stopThisThing)
    while run {
        print ("Running...")
        usleep(100000)
    }
}

最后的繁忙循环永远不会停止。

标签: swiftgrand-central-dispatch

解决方案


您的测试while循环将阻塞主线程(如果测试在主线程上运行)。因此,通过调度的闭包DispatchQueue.main.async永远不会运行,并且您的run布尔值永远不会被重置。这会导致死锁。Thread.isMainThread您可以通过打印或添加类似的测试来确认这一点dispatchPrecondition(condition: .onQueue(.main))

幸运的是,单元测试有一个简单的机制可以避免这种死锁。如果您希望单元测试等待某个异步过程,请使用XCTestExpectation

func testAsyncBar() throws {
    let e = expectation(description: "asyncBar")

    func stopThisThing(ints: [Int]) {
        e.fulfill()
    }

    Foo.asyncBar(completion: stopThisThing)

    waitForExpectations(timeout: 5)
}

这避免了循环引入的问题while,否则会阻塞线程。

请参阅使用期望测试异步操作


推荐阅读