Как выполнить модульное тестирование асинхронных API? - PullRequest
64 голосов
/ 29 января 2010

Я установил Google Toolbox для Mac в Xcode и следовал инструкциям по настройке модульного тестирования, найденных здесь .

Все это прекрасно работает, и я могу тестировать свои синхронные методы на всех моих объектах абсолютно нормально. Однако большинство сложных API-интерфейсов, которые я на самом деле хочу протестировать, возвращают результаты асинхронно, вызывая метод для делегата - например, вызов системы загрузки и обновления файлов сразу же возвращается, а затем запускает -fileDownloadDidComplete: метод после завершения загрузки файла. .

Как бы я проверил это как юнит-тест?

Похоже, я бы хотел, чтобы функция testDownload или, по крайней мере, тестовая среда ожидала запуска fileDownloadDidComplete: метод.

РЕДАКТИРОВАТЬ: Теперь я переключился на использование встроенной системы XCode XCTest и обнаружил, что TVRSMonitor на Github предоставляет простой и удобный способ использования семафоров для ожидания завершения асинхронных операций.

Например:

- (void)testLogin {
  TRVSMonitor *monitor = [TRVSMonitor monitor];
  __block NSString *theToken;

  [[Server instance] loginWithUsername:@"foo" password:@"bar"
                               success:^(NSString *token) {
                                   theToken = token;
                                   [monitor signal];
                               }

                               failure:^(NSError *error) {
                                   [monitor signal];
                               }];

  [monitor wait];

  XCTAssert(theToken, @"Getting token");
}

Ответы [ 13 ]

1 голос
/ 15 апреля 2013

Я только что написал запись в блоге об этом (фактически я начал блог, потому что я думал, что это была интересная тема). В итоге я использовал метод swizzling, чтобы можно было вызывать обработчик завершения, используя любые аргументы, которые я хочу, не дожидаясь, что казалось хорошим для модульного тестирования. Примерно так:

- (void)swizzledGeocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler
{
    completionHandler(nil, nil); //You can test various arguments for the handler here.
}

- (void)testGeocodeFlagsComplete
{
    //Swizzle the geocodeAddressString with our own method.
    Method originalMethod = class_getInstanceMethod([CLGeocoder class], @selector(geocodeAddressString:completionHandler:));
    Method swizzleMethod = class_getInstanceMethod([self class], @selector(swizzledGeocodeAddressString:completionHandler:));
    method_exchangeImplementations(originalMethod, swizzleMethod);

    MyGeocoder * myGeocoder = [[MyGeocoder alloc] init];
    [myGeocoder geocodeAddress]; //the completion handler is called synchronously in here.

    //Deswizzle the methods!
    method_exchangeImplementations(swizzleMethod, originalMethod);

    STAssertTrue(myGeocoder.geocoded, @"Should flag as geocoded when complete.");//You can test the completion handler code here. 
}

запись в блоге для всех, кому небезразлично.

0 голосов
/ 28 января 2014

Я нашел эту статью на этом, который является muc http://dadabeatnik.wordpress.com/2013/09/12/xcode-and-asynchronous-unit-testing/

0 голосов
/ 31 мая 2012

Мой ответ таков: модульное тестирование концептуально не подходит для тестирования асинхронных операций. Асинхронная операция, такая как запрос к серверу и обработка ответа, происходит не в одном блоке, а в двух блоках.

Чтобы связать ответ с запросом, вы должны либо каким-либо образом заблокировать выполнение между двумя модулями, либо поддерживать глобальные данные. Если вы блокируете выполнение, то ваша программа не выполняется нормально, и если вы поддерживаете глобальные данные, вы добавили постороннюю функциональность, которая сама может содержать ошибки. Любое решение нарушает саму идею модульного тестирования и требует от вас вставки специального кода тестирования в ваше приложение; а затем, после модульного тестирования, вам все равно придется отключить код тестирования и выполнить старомодное «ручное» тестирование. Время и усилия, потраченные на модульное тестирование, при этом, по крайней мере, частично теряются.

...