在从 XCTestCase 调用的代码中使用 Dispatch.main 不起作用

Using Dispatch.main in code called from XCTestCase does not work

我有一个函数,它是同步函数的异步包装器。

同步函数如下:

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

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

你的测试的 while 循环会阻塞主线程(如果测试 运行s 在主线程上)。因此,通过 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 循环引入的问题,否则会阻塞线程。

Testing Asynchronous Operations with Expectations