单元测试中的比赛条件

时间:2018-06-29 08:43:42

标签: swift unit-testing race-condition xctest

我目前正在测试一些类的类,这些类可以进行诸如REST API调用之类的网络工作,并且Realm数据库在此过程中发生了变异。当我一次运行所有不同的测试时,就会出现竞赛条件(但是,当然,当我一一运行它们时,它们全部通过了)。如何可靠地使测试通过?

我试图像这样在GCD块中调用上述功能:

@t.content

我的一项测试仍然失败,因此我猜以上方法无效。我启用了Thread Sanitizer,它会不时报告出现竞争情况。

我无法发布代码,所以我正在寻找概念性的解决方案。

1 个答案:

答案 0 :(得分:1)

通常是某种形式的依赖注入。它是DispatchQueue的内部公开var,带有队列的函数中的默认参数或构造函数参数。您只需要通过某种方法即可通过测试队列,该队列可以在需要时调度事件。

DispatchQueue.main.async会将块异步调度到主队列上的被调用者,因此在您声明时无法保证。

示例(免责声明:我是从内存中键入内容的,因此它可能无法编译,但给出了提示):

// In test code.
struct TestQueue: DispatchQueue {

    // make sure to impement other necessary protocol methods

    func async(block: () -> Void) {
        // you can even have some different behavior for when to execute the block. 
        // also you can pass XCTestExpectations to this TestQueue to be fulfilled if necessary.
        block()
    }
}

// In source code. In test, pass the Test Queue to the first argument
func doSomething(queue: DispatchQueue = DispatchQueue.main, completion: () -> Void) {
    queue.async(block: completion)
}

其他测试异步和消除竞争条件的方法围绕着巧妙地实现XCTestExpectation。

如果您有权访问最终调用的完成块:

// In source 
class Subject {

    func doSomethingAsync(completion: () -> Void) {
        ...
    }

}

// In test

func testDoSomethingAsync() {
    let subject = Subject()
    let expect = expectation(description: "does something asnyc")
    subject.doSomethingAsync {
        expect.fulfill()
    }

    wait(for: [expect], timeout: 1.0)
    // assert something here
    // or the wait may be good enough as it will fail if not fulfilled
}

如果您无权访问完成块,则通常意味着找到一种方法来注入或子类化测试双重对象,您可以将其设置为XCTestExpectation并最终在异步工作完成后达到期望。