我在Xcode 6中编写集成测试,与我的单元和功能测试一起进行。 XCTest有一个setUp()方法,在每次测试之前被调用。大!
它还有XCTestException,它允许我写异步测试。也很棒!
但是,我想在每次测试之前使用测试数据填充测试数据库,而setUp只是在异步数据库调用完成之前开始执行测试。
有没有办法让setUp等到我的数据库准备好运行测试之前呢?
这是我现在所做的一个例子。由于setUp在数据库完成填充之前返回,我必须每次测试复制很多测试代码:
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中执行异步操作的情况下,应该使用信号量技术:
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 = dispatch_semaphore_create(0) db.overwriteDatabase(data) { // do some stuff dispatch_semaphore_signal(semaphore) } dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER) }
注意,为了工作,这个onDone块不能在主线程上运行(否则你会死锁)。
如果此onDone块在主队列上运行,则可以使用运行循环:
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 { NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode,beforeDate: NSDate.distantFuture()) } }
这是一个非常低效的模式,但是根据overwriteDatabase的实现方式,可能是必要的
注意,只有当你知道onDone块在主线程上运行时,才使用这种模式(否则你必须做一些完成变量的同步)。