我目前正在测试一些类,这些类执行 REST API 调用等网络操作,并且 Realm 数据库在此过程中发生了变化。当我同时运行所有不同的测试时,会出现竞争条件(当然,当我一个一个地运行它们时,它们都会通过)。我怎样才能可靠地通过测试?
我试过像这样在 GCD block 中调用提到的函数:
DispatchQueue.main.async {
self.function.start()
}
我的一个测试仍然失败,所以我猜上面的方法没有用。我已启用 Thread Sanitizer,它会不时报告出现竞争条件。
我无法发布代码,所以我正在寻找概念性解决方案。
最佳答案
通常是某种形式的依赖注入(inject)。它可以是 DispatchQueue 的内部公开 var,带有队列的函数中的默认参数,或者构造函数参数。您只需要一些方法来传递一个测试队列,该队列在您需要时分派(dispatch)事件。
DispatchQueue.main.async
会将 block 异步安排到主队列上的被调用方,因此在您进行断言时无法保证。
示例(免责声明:我是凭内存输入的,所以它可能无法编译,但它给出了想法):
// 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。
如果您有权访问最终调用的完成 block :
// 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
}
如果您无权访问完成 block ,这通常意味着找到一种方法来注入(inject)或子类化您可以设置 XCTestExpectation 的测试替身,并最终在异步工作完成时满足预期。
关于swift - 单元测试中的竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51097682/