xcode - funcionales - herramientas para pruebas de carga y rendimiento
¿Cómo puedo hacer que XCTest espere llamadas asincrónicas en setUp antes de ejecutar las pruebas? (2)
En lugar de usar semáforos o bucles de bloqueo, puede usar la misma función waitForExpectationsWithTimeout:handler:
que usa en sus casos de prueba asíncrona.
// 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];
}
Estoy escribiendo pruebas de integración en Xcode 6 para acompañar mi unidad y las pruebas funcionales. XCTest tiene un método setUp () que se llama antes de cada prueba. ¡Estupendo!
También tiene XCTestException que me permite escribir pruebas asíncronas. ¡También genial!
Sin embargo, me gustaría llenar mi base de datos de prueba con datos de prueba antes de cada prueba y setUp solo comienza a ejecutar las pruebas antes de que se realice la llamada a la base de datos asincrónica.
¿Hay alguna manera de esperar hasta que mi base de datos esté lista antes de ejecutar las pruebas?
Aquí hay un ejemplo de lo que tengo que hacer ahora. Desde que setUp regresa antes de que la base de datos termine de poblar, tengo que duplicar una gran cantidad de código de prueba en cada prueba:
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)
}
}
}
Esto es lo que me gustaría:
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)
}
}
}
}
Hay dos técnicas para ejecutar pruebas asíncronas. XCTestExpectation
y semáforos. En el caso de hacer algo asincrónico en setUp
, debe usar la técnica de semáforo:
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)
}
Tenga en cuenta que, para que funcione, este bloque onDone
no se puede ejecutar en el hilo principal (de lo contrario, se bloqueará).
Si este bloque onDone
se ejecuta en la cola principal, puede usar los bucles de ejecución:
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())
}
}
Este es un patrón muy ineficiente, pero dependiendo de cómo se implementó overwriteDatabase
, podría ser necesario
Tenga en cuenta que solo use este patrón si sabe que el bloque onDone
se ejecuta en el hilo principal (de lo contrario, tendrá que hacer alguna sincronización de la variable finished
).