Ошибка с XCTestExpectation: нарушение API - несколько вызовов, сделанных для - [XCTestExpectation выполнить]
Я использую XCTestExpectations в Xcode 6 (Beta 5) для асинхронного тестирования. Все мои асинхронные тесты проходят индивидуально каждый раз, когда я их запускаю. Однако, когда я пытаюсь запустить весь пакет, некоторые тесты не проходят, и приложение выходит из строя.
Ошибка, которую я получаю, говорит API violation - multiple calls made to -[XCTestExpectation fulfill]
. Действительно, это не верно в рамках одного метода; мой общий формат для моих тестов показан ниже:
- (void) someTest {
/* Declare Expectation */
XCTestExpectation *expectation = [self expectationWithDescription:@"My Expectation"];
[MyClass loginOnServerWithEmail:@"[email protected]" andPassword:@"asdfasdf" onSuccess:^void(User *user) {
/* Make some assertions here about the object that was given. */
/* Fulfill the expectation */
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
/* Error handling here */
}];
}
Опять же, эти тесты проходят, когда запускаются индивидуально, и на самом деле они выполняют сетевые запросы (работают точно так, как предполагалось), но вместе, сбор тестов не запускается.
Я смог посмотреть этот пост здесь, но не смог заставить решение работать для меня.
Кроме того, я запускаю OSX Mavericks и использую Xcode 6 (Beta 5).
Ответы
Ответ 1
Я не думаю, что использование __weak
или __block
- хороший подход. Я написал много модульных тестов, используя XCTestExpectation
для awhile и никогда не сталкивался с этой проблемой до сих пор. Я наконец выяснил, что это настоящая причина проблемы, которая потенциально может вызвать ошибки в моем приложении. Основная причина моей проблемы заключается в том, что startAsynchronousTaskWithDuration
вызывает многократное выполнение completeHandler. После того, как я исправлю это, нарушение API исчезло!
[self startAsynchronousTaskWithDuration:4 completionHandler:^(id result, NSError *error) {
XCTAssertNotNil(result);
XCTAssertNil(error);
[expectation fulfill];
}];
Хотя мне потребовалось несколько часов, чтобы исправить мои модульные тесты, но я пришел к пониманию ошибки нарушения API, которая поможет мне избежать будущей проблемы времени выполнения в моем приложении.
Ответ 2
Я думаю, вероятно, что у вас есть проблема с циклом сохранения, которая мешает выпустить ваш объект, который вызывает блок, в котором ожидание выполняется несколько раз.
В противном случае, если ожидается, что ваше ожидание будет вызвано несколько раз, я написал небольшое расширение, позволяющее указать количество ожиданий:
import XCTest
extension XCTestExpectation {
private class IntWrapper {
let value :Int!
init?(value:Int?) {
self.value = value
if (value == nil) {
return nil
}
}
}
private struct AssociatedKey {
static var expectationCountKey = ""
}
var expectationCount:Int? {
get {
return objc_getAssociatedObject(self, &AssociatedKey.expectationCountKey) as? Int
}
set {
objc_setAssociatedObject(self, &AssociatedKey.expectationCountKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
func decrementalFulfill() {
guard let expectationCount = self.expectationCount else {
fulfill()
return
}
self.expectationCount = expectationCount - 1
if self.expectationCount <= 0 {
fulfill()
}
}
}
полный код (с тестом:) здесь: https://gist.github.com/huguesbr/7d110bffd043e4d11f2886693c680b06
Ответ 3
Вот, вероятно, ответ, который вы ищете:
XCTestExpectation: как избежать вызова метода выполнения после окончания контекста ожидания?
Это по крайней мере исправило проблему для меня.
Ответ 4
Попробуйте объявить ваше ожиданиеWithDescription слабым и разворачивать необязательную "ожидаемую" переменную.
weak var asyncExpectation = expectationWithDescription("expectation")
check for options in the block.
if let asyncExpectation = asyncExpectation{
asyncExpectation.fulfill()
}
Это позволяет избежать освобождения переменной asyncExpectation и вызывать ваше ожидание на ноль.