Как к модульному тесту асинхронные API?

Я установил Google Toolbox for Mac into 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");
}

64
задан Ben Clayton 28 October 2013 в 17:36
поделиться

3 ответа

Я столкнулся с тем же вопросом и нашел другое решение, которое мне подходит.

Я использую подход «старой школы» для превращения асинхронных операций в поток синхронизации, используя семафор следующим образом:

// create the object that will perform an async operation
MyConnection *conn = [MyConnection new];
STAssertNotNil (conn, @"MyConnection init failed");

// create the semaphore and lock it once before we start
// the async operation
NSConditionLock *tl = [NSConditionLock new];
self.theLock = tl;
[tl release];    

// start the async operation
self.testState = 0;
[conn doItAsyncWithDelegate:self];

// now lock the semaphore - which will block this thread until
// [self.theLock unlockWithCondition:1] gets invoked
[self.theLock lockWhenCondition:1];

// make sure the async callback did in fact happen by
// checking whether it modified a variable
STAssertTrue (self.testState != 0, @"delegate did not get called");

// we're done
[self.theLock release]; self.theLock = nil;
[conn release];

Затем обязательно вызовите

[self.theLock unlockWithCondition:1];

в делегате (ах).

52
ответ дан 24 November 2019 в 15:44
поделиться

Это сложно. Я думаю, что в вашем тесте вам понадобится установить runloop, а также возможность указать этот runloop в вашем асинхронном коде. В противном случае обратных вызовов не будет, так как они выполняются в цикле.

Думаю, вы можете просто запустить цикл на короткое время в цикле. И позволить обратному вызову установить некоторую общую переменную состояния. Или, может быть, просто попросить обратный вызов прервать цикл. Таким образом, вы знаете, что тест окончен. Вы должны быть в состоянии проверить таймауты, остановив цикл через некоторое время. Если это случится, таймаут будет отменен.

Я никогда этого не делал, но думаю, скоро придется. Пожалуйста, поделитесь своими результатами :-)

.
8
ответ дан 24 November 2019 в 15:44
поделиться

ST3FAN, вы гений. Большое спасибо!

Так я сделал это, используя ваше предложение.

«Downloader» определяет протокол с методом DownloadDiDIdComplete, который пожали на завершения. Существует переменная элемента Bool «DownloadComplete», которая используется для завершения цикла запуска.

-(void) testDownloader {
 downloadComplete = NO;
 Downloader* downloader = [[Downloader alloc] init] delegate:self];

 // ... irrelevant downloader setup code removed ...

 NSRunLoop *theRL = [NSRunLoop currentRunLoop];

 // Begin a run loop terminated when the downloadComplete it set to true
 while (!downloadComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

}


-(void) DownloaderDidComplete:(Downloader*) downloader withErrors:(int) errors {
    downloadComplete = YES;

    STAssertNotEquals(errors, 0, @"There were errors downloading!");
}

Руководитель может потенциально бегать вечно, конечно,. Я улучшу, что позже!

19
ответ дан 24 November 2019 в 15:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: