Безопасно ли вызывать метод выполнения XCTestExpectation в фоновом потоке?

Я использую XCTestExpectation во многих тестах, и иногда (очень случайно) некоторые ожидания не оправдываются (хотя я уверен, что они должны быть).

При исследовании этой проблемы я заметил, что некоторые ожидания выполняются в основном потоке, а некоторые — в фоновом. И пока эти проблемы с отработанными в фоновом потоке.

Безопасно ли оправдывать ожидания от фонового потока? Я не мог найти никакой явной информации об этом.

Ниже приведен пример того, как я использую XCTestExpectation:

__block XCTestExpectation *expectation = [self expectationWithDescription:@"test"];

[self doSomethingAsyncInBackgroundWithSuccess:^{
    [expectation fullfill];
}];

[self waitForExpectationsWithTimeout:10.0 handler:^(NSError *error) {
    expectation = nil;
    if (error) {
        NSLog(@"Timeout Error: %@", error);
    }
}];

person Piotr    schedule 12.01.2016    source источник
comment
Вы действительно должны синхронизировать свое взаимодействие с XCTestExpectation, чтобы избежать состояния гонки, присутствующего в приведенном выше коде, но я лично подозреваю, что проблема заключается в другом. Вы можете подтвердить это, временно добавив некоторые операторы NSLog и просмотрев временные метки, и я подозреваю, что вы обнаружите, что ваш блок тайм-аута ожидания действительно вызывается до вызова блока завершения вашего асинхронного метода.   -  person Rob    schedule 12.01.2016
comment
Я очень сомневаюсь, что выполнение XCTestExpectation из фонового потока небезопасно. Весь смысл XCTestExpectation заключается в запуске асинхронных тестов.   -  person Peter Schorn    schedule 14.10.2020


Ответы (1)


Нигде не задокументировано, что XCTestExpectation является потокобезопасным. из-за отсутствия официальной документации по этому вопросу вы можете только догадываться, создавая тестовые примеры:

- (void)testExpectationMainThread;
{

  __block XCTestExpectation *expectation = [self expectationWithDescription:@"test"];

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [expectation fulfill];
  });

  [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) {
    NSLog(@"%@", error);
  }];

}

- (void)testExpectationStartMainThreadFulfilBackgroundThread;
{

  __block XCTestExpectation *expectation = [self expectationWithDescription:@"test"];

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, kNilOptions), ^{
    [expectation fulfill];
  });

  [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) {
    NSLog(@"%@", error);
  }];

}

- (void)testExpectationBackgroundThread;
{
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, kNilOptions);

  __block XCTestExpectation *expectation;


  dispatch_sync(queue, ^{
    expectation = [self expectationWithDescription:@"test"];
  });

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), queue, ^{
    [expectation fulfill];
  });

  [self waitForExpectationsWithTimeout:2 handler:^(NSError * _Nullable error) {
    NSLog(@"%@", error);
  }];

}

Здесь он не вылетает и не вызывает проблем, однако из-за отсутствия официальной документации, вероятно, безопаснее придерживаться той же очереди для выполнения.


вам действительно следует заглушить метод doSomethingAsyncInBackgroundWithSuccess и предоставить приложению локальные «фиктивные» данные.

Ваши модульные тесты не должны полагаться на сеть, поскольку она является переменной.


Вы должны выполнять блок завершения doSomethingAsyncInBackgroundWithSuccess в основном потоке (или, по крайней мере, предоставить способ последовательного обратного вызова в том же потоке), вы можете легко сделать это с помощью GCD.

- (void)doSomethingAsyncInBackgroundWithSuccess:(void (^)(void))completion;
{
  dispatch_async(dispatch_get_main_queue(), ^{
    completion();
  });
}

или используйте NSOperationQueue mainQueue

- (void)doSomethingAsyncInBackgroundWithSuccess:(void (^)(void))completion;
{
  [NSOperationQueue.mainQueue addOperationWithBlock:^{
    completion();
  }];
}
person Oliver Atkinson    schedule 12.01.2016
comment
Спасибо за ответ. На самом деле ожидание, с которым у меня проблемы, не связано с сетевыми вызовами. Это асинхронная операция, но она не выполняет сетевые операции. Я знаю, что могу переключиться на основной поток, и я попробую это. Проблема в том, что я не уверен, что выполнение в фоновом потоке явно запрещено и является ли это корнем этой проблемы. Я просто предполагаю, что это может быть проблемой, к сожалению. Может быть, у вас тоже были подобные проблемы, и эта отправка на главную исправила это? - person Piotr; 12.01.2016
comment
@Piotr нигде не задокументировано, что XCTestExpectation является потокобезопасным - person Oliver Atkinson; 12.01.2016
comment
Что ж, вы правы. Возможно, это означает, что это не так. Я попытаюсь отправить в основной поток, и, надеюсь, проблемы исчезнут. - person Piotr; 12.01.2016
comment
@Piotr Я добавил несколько примеров тестовых случаев - вы можете использовать их для проверки любых претензий, которые у вас могут быть для вашего кода. - person Oliver Atkinson; 12.01.2016