Как я могу unit test передать делегат NSURLConnection? Я создал класс ConnectionDelegate, который соответствует различным протоколам, чтобы обслуживать данные из Интернета для разных ViewControllers. Прежде чем зайти слишком далеко, я хочу начать писать модульные тесты. Но я не знаю, как тестировать их как единицу без подключения к Интернету. Я хотел бы также, что я должен сделать, чтобы рассматривать асинхронные обратные вызовы.
Как unit test делегат NSURLConnection?
Ответ 1
Это похоже на ответ Джона, но не помещал его в комментарий. Первый шаг - убедиться, что вы не создаете реальное соединение. Самый простой способ добиться этого - вывести создание соединения в метод factory, а затем заменить метод factory в своем тесте. При частичной макетной поддержке OCMock это может выглядеть так.
В вашем реальном классе:
- (NSURLConnection *)newAsynchronousRequest:(NSURLRequest *)request
{
return [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
В вашем тесте:
id objectUnderTest = /* create your object */
id partialMock = [OCMockObject partialMockForObject:objectUnderTest];
NSURLConnection *dummyUrlConnection = [[NSURLConnection alloc]
initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"file:foo"]]
delegate:nil startImmediately:NO];
[[[partialMock stub] andReturn:dummyUrlConnection] newAsynchronousRequest:[OCMArg any]];
Теперь, когда ваш тестируемый объект пытается создать URL-соединение, он фактически получает фиктивное соединение, созданное в тесте. Фиктивное соединение не должно быть действительным, потому что мы его не запускаем, и оно никогда не используется. Если ваш код действительно использует соединение, вы можете вернуть еще один макет, который издевается над NSURLConnection.
Второй шаг - вызвать метод на вашем объекте, который инициирует создание NSURLConnection:
[objectUnderTest doRequest];
Поскольку тестируемый объект не использует реальное соединение, мы теперь можем вызвать методы делегата из теста. Для NSURLResponse мы используем другой макет, данные ответа создаются из строки, определенной в другом месте теста:
int statusCode = 200;
id responseMock = [OCMockObject mockForClass:[NSHTTPURLResponse class]];
[[[responseMock stub] andReturnValue:OCMOCK_VALUE(statusCode)] statusCode];
[objectUnderTest connection:dummyUrlConnection didReceiveResponse:responseMock];
NSData *responseData = [RESPONSE_TEXT dataUsingEncoding:NSASCIIStringEncoding];
[objectUnderTest connection:dummyUrlConnection didReceiveData:responseData];
[objectUnderTest connectionDidFinishLoading:dummyUrlConnection];
Что это. Вы эффективно фальсифицировали все взаимодействия, с которыми тестируется объект с соединением, и теперь вы можете проверить, находится ли он в состоянии, в котором он должен находиться.
Если вы хотите увидеть какой-то "настоящий" код, посмотрите на тесты для класса из проекта CCMenu, который использует NSURLConnections. Это немного запутанно, потому что тестируемый класс также называется связью.
Ответ 2
EDIT (2-18-2014): Я просто наткнулся на эту статью с более элегантным решением.
http://www.infinite-loop.dk/blog/2011/04/unittesting-asynchronous-network-access/
По существу, у вас есть следующий метод:
- (BOOL)waitForCompletion:(NSTimeInterval)timeoutSecs {
NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs];
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
if([timeoutDate timeIntervalSinceNow] < 0.0)
break;
} while (!done);
return done;
}
В конце вашего тестового метода вы убедитесь, что события не были исчерпаны:
STAssertTrue([self waitForCompletion:5.0], @"Timeout");
Основной формат:
- (void)testAsync
{
// 1. Call method which executes something asynchronously
[obj doAsyncOnSuccess:^(id result) {
STAssertNotNil(result);
done = YES;
}
onError:^(NSError *error) [
STFail();
done = YES;
}
// 2. Determine timeout
STAssertTrue([self waitForCompletion:5.0], @"Timeout");
}
==============
Я опаздываю на вечеринку, но натолкнулся на очень простое решение. (Большое спасибо http://www.cocoabuilder.com/archive/xcode/247124-asynchronous-unit-testing.html)
.h файл:
@property (nonatomic) BOOL isDone;
.m file:
- (void)testAsynchronousMethod
{
// 1. call method which executes something asynchronously.
// 2. let the run loop do its thing and wait until self.isDone == YES
self.isDone = NO;
NSDate *untilDate;
while (!self.isDone)
{
untilDate = [NSDate dateWithTimeIntervalSinceNow:1.0]
[[NSRunLoop currentRunLoop] runUntilDate:untilDate];
NSLog(@"Polling...");
}
// 3. test what you want to test
}
isDone
устанавливается в YES
в потоке, выполняемом асинхронным методом.
Итак, в этом случае я создал и запустил NSURLConnection на шаге 1 и сделал его делегатом этот тестовый класс. В
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
Я установил self.isDone = YES;
. Мы выходим из цикла while и выполняем тест. Готово.
Ответ 3
Я избегаю общения в модульных тестах. Вместо этого:
- Я изолирую NSURLConnection внутри метода.
- Я создаю подкласс тестирования, переопределяя этот метод, чтобы удалить все следы NSURLConnection.
- Я пишу один тест, чтобы убедиться, что этот метод будет вызван, когда захочу. Тогда я знаю, что в реальной жизни он сгорит NSURLConnection.
Затем я сосредоточусь на более интересной части: синтезируйте mock NSURLResponses с различными характеристиками и передайте их методам NSURLConnectionDelegate.
Ответ 4
Мой любимый способ сделать это - подкласс NSURLProtocol и отвечать на все HTTP-запросы - или другие протоколы. Затем вы регистрируете протокол тестирования в методе -setup
и отменяете его в своем методе -tearDown
.
Затем вы можете использовать этот тестовый протокол для получения некоторых хорошо известных данных, чтобы вы могли проверить его в своих модульных тестах.
Я написал несколько статей в блогах по этому вопросу. Наиболее важным для вашей проблемы, вероятно, будет Использование NSURLProtocol для вставки тестовых данных и Тестирование асинхронного сетевого доступа в блоке.
Вы также можете посмотреть мой ILCannedURLProtocol, который описан в предыдущих статьях. Источник доступен в Github.
Ответ 5
вы можете дать шанс:
https://github.com/marianoabdala/ZRYAsyncTestCase
Здесь приведен пример примерного кода модуля NSURLConnection: https://github.com/marianoabdala/ZRYAsyncTestCase/blob/12a84c7f1af1a861f76c7825aef6d9d6c53fd1ca/SampleProject/SampleProjectTests/SampleProjectTests.m#L33-L56
Ответ 6
Вот что я делаю:
- Получить панель управления XAMPP http://www.apachefriends.org/en/xampp.html
- Запустите сервер Apache
- В вашей папке
~/Sites
поместите тестовый файл (любые данные, которые вы хотите, мы назовем егоmy file.test
). - Запустите свой делегат, используя URL http://localhost/~username/myfile.test
- Остановить сервер Apache, если он не используется.