一尘不染

如何在运行测试之前让XCTest等待setUp中的异步调用?

swift

我正在用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)
            }
        }

    }

}

阅读 301

收藏
2020-07-07

共1个答案

一尘不染

有两种运行异步测试的技术。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 = DispatchSemaphore(value: 0)

    db.overwriteDatabase(data) {

        // do some stuff

        semaphore.signal()
    }

    semaphore.wait()
}

请注意,要使其正常工作,该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 {
        RunLoop.current.run(mode: .default, before: Date.distantFuture)
    }
}

这是一个非常低效的模式,但是根据overwriteDatabase实现方式的不同,可能有必要

注意,仅当您知道该onDone块在主线程上运行时才使用此模式(否则,您将必须对finished变量进行一些同步)。

2020-07-07