Подтвердить что ты не робот

Как я могу заставить XCTest ждать асинхронных вызовов в setUp до запуска тестов?

Я пишу интеграционные тесты в 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)
            }
        }

    }

}
4b9b3361

Ответ 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 = 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 работает в основном потоке (в противном случае вам потребуется выполнить некоторую синхронизацию переменной finished).

Ответ 2

Вместо использования семафоров или циклов блокировки вы можете использовать ту же функцию waitForExpectationsWithTimeout:handler:, которую вы используете в своих асинхронных тестовых случаях.

// Swift
override func setUp() {
    super.setUp()

    let exp = expectation(description: "\(#function)\(#line)")

    // Issue an async request
    let data = getData()
    db.overwriteDatabase(data) {
        // do some stuff
        exp.fulfill()
    }

    // Wait for the async request to complete
    waitForExpectations(timeout: 40, handler: nil)
}

// Objective-C
- (void)setUp {
    [super setUp];

    NSString *description = [NSString stringWithFormat:@"%s%d", __FUNCTION__, __LINE__];
    XCTestExpectation *exp = [self expectationWithDescription:description];

    // Issue an async request
    NSData *data = [self getData];
    [db overwriteDatabaseData: data block: ^(){
        [exp fulfill];
    }];        

    // Wait for the async request to complete
    [self waitForExpectationsWithTimeout:40 handler: nil];
}