Модульное тестирование с помощью NSURLConnection
-
03-07-2019 - |
Вопрос
Я хочу протестировать фрагмент кода, который использует сеть (the NSURLConnection
класс, если быть точным).Код (давайте назовем его NetworkManager
) выглядит немного так:
- (id) buildConnection
{
// some more code and then:
return [NSURLConnection …];
}
- (void) startNetworkSync
{
id connection = [self buildConnection];
//…
}
В модульном тестировании я хотел бы избавиться от сети, т.е.замените NSURLConnection
объект с помощью макета.Как мне это сделать?
Я попытался создать частичный макет NetworkManager
это заменило бы buildConnection
метод с помощью заглушки.Проблема в том, что частичные насмешки выполняются с помощью Окмок только заглушки сообщений из внешнего мира – отправка buildConnection
из самого startNetworkSync
вызывает исходный метод, а не заглушку.
Я также попробовал обезьянье исправление NetworkManager
класс через категорию.Это работает, я могу легко переопределить buildConnection
метод другим кодом и замените реальный NSURLConnection
с заглушкой.Проблема в том, что я не нашел простого способа получить заглушенное соединение в тесте – соединение является закрытой частью NetworkManager
.
Тогда я мог бы подклассировать NetworkManager
, переопределить buildConnection
метод и добавьте переменную экземпляра плюс средство доступа для созданного соединения.Тем не менее, это похоже на большой объем кода.
Как бы вы решили эту проблему?Я ищу решение, которое сохранит NetworkManager
дизайн класса чистый и не требует много магии или большого количества кода в тесте.
Решение
Это именно та проблема, для решения которой предназначена инъекция зависимостей;если вы используете startNetworkSyncWithConnection:(NSURLConnection*)
вместо этого вы можете легко протестировать метод с помощью фиктивного соединения.Если вы не хотите менять API для своих клиентов, вы могли бы даже сохранить startNetworkSync
как оболочка, которая ничего не делает, но вызывает этот новый метод с [self buildConnection]
в качестве аргумента.
Другие советы
Я модифицировал OCMock для поддержки реальных частичных макетов, см. репо на GitHub.
Другое решение, которое я недавно использовал, - это полностью абстрагировать сетевой интерфейс.Если классу нужны какие-то данные из сети, он, вероятно, взаимодействует с какой-либо серверной службой, которая может быть явно смоделирована как протокол:
@protocol SomeNetworkService
- (NSArray*) allAvailableFoos;
- (void) insertNewFoo: (Foo*) foo;
@end
И тогда у вас будет реальная HTTP-реализация и тестовая.Это означает больше работы, но также и гораздо лучшую тестируемость.Тесты менее хрупкие и гораздо удобнее, поскольку сетевой уровень тестирования может делать все, что вам нужно.