如何在运行测试之前让XCTest等待setUp中的异步调用?
我正在用Xcode 6编写集成测试,以配合单元测试和功能测试. XCTest有一个setUp()方法,该方法在每次测试之前都会被调用.伟大的!
I'm writing integration tests in Xcode 6 to go alongside my unit and functional tests. XCTest has a setUp() method that gets called before every test. Great!
它也具有XCTestException,可以让我编写异步测试.也很棒!
It also has XCTestException's which let me write async tests. Also great!
但是,我想在每次测试之前将测试数据填充到测试数据库中,然后setUp才开始执行测试,然后再执行异步数据库调用.
However, I would like to populate my test database with test data before every test and setUp just starts executing tests before the async database call is done.
是否有一种方法可以让setUp等到数据库准备就绪后再运行测试?
Is there a way to have setUp wait until my database is ready before it runs tests?
这是我现在所做的一个例子.由于setUp在数据库完成填充之前返回,因此我必须在每个测试中重复很多测试代码:
Here's an example of what I have do now. Since setUp returns before the database is done populating I have to duplicate a lot of test code every test:
func test_checkSomethingExists() {
let expectation = expectationWithDescription("")
var expected:DatabaseItem
// Fill out a database with data.
var data = getData()
overwriteDatabase(data, {
// Database populated.
// Do test... in this pseudocode I just check something...
db.retrieveDatabaseItem({ expected in
XCTAssertNotNil(expected)
expectation.fulfill()
})
})
waitForExpectationsWithTimeout(5.0) { (error) in
if error != nil {
XCTFail(error.localizedDescription)
}
}
}
这就是我想要的:
class MyTestCase: XCTestCase {
override func setUp() {
super.setUp()
// Fill out a database with data. I can make this call do anything, here
// it returns a block.
var data = getData()
db.overwriteDatabase(data, onDone: () -> () {
// When database done, do something that causes setUp to end
// and start running tests
})
}
func test_checkSomethingExists() {
let expectation = expectationWithDescription("")
var expected:DatabaseItem
// Do test... in this pseudocode I just check something...
db.retrieveDatabaseItem({ expected in
XCTAssertNotNil(expected)
expectation.fulfill()
})
waitForExpectationsWithTimeout(5.0) { (error) in
if error != nil {
XCTFail(error.localizedDescription)
}
}
}
}
有两种运行异步测试的技术. XCTestExpectation
和信号灯.如果在setUp
中执行异步操作,则应使用信号量技术:
There are two techniques for running asynchronous tests. XCTestExpectation
and semaphores. In the case of doing something asynchronous in setUp
, you should use the semaphore technique:
override func setUp() {
super.setUp()
// Fill out a database with data. I can make this call do anything, here
// it returns a block.
let data = getData()
let semaphore = DispatchSemaphore(value: 0)
db.overwriteDatabase(data) {
// do some stuff
semaphore.signal()
}
semaphore.wait()
}
请注意,为了起作用,该onDone
块不能在主线程上运行(否则将导致死锁).
Note, for that to work, this onDone
block cannot run on the main thread (or else you'll deadlock).
如果此onDone
块在主队列上运行,则可以使用运行循环:
If this onDone
block runs on the main queue, you can use run loops:
override func setUp() {
super.setUp()
var finished = false
// Fill out a database with data. I can make this call do anything, here
// it returns a block.
let data = getData()
db.overwriteDatabase(data) {
// do some stuff
finished = true
}
while !finished {
RunLoop.current.run(mode: .default, before: Date.distantFuture)
}
}
这是一个非常低效的模式,但是根据overwriteDatabase
的实施方式,可能有必要
This is a very inefficient pattern, but depending upon how overwriteDatabase
was implemented, it might be necessary
注意,仅当您知道onDone
块在主线程上运行时才使用此模式(否则,您将必须对finished
变量进行一些同步).
Note, only use this pattern if you know that onDone
block runs on the main thread (otherwise you'll have to do some synchronization of finished
variable).