IOS VIPER: Как протокол помогает в модульном тестировании?

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

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

Разве мы не можем добиться того же с помощью метода обратного вызова с использованием блоков? Надеюсь, кто-то может помочь мне понять с точки зрения использования на каком-то примере.

Ваше здоровье


person MaK    schedule 17.12.2016    source источник


Ответы (2)


Использование обратного вызова, например. от Interactor к Presenter, может затруднить тестирование Presenter.

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

Если Presenter реализует протокол, определенный Interactor, ваш тест может просто напрямую вызвать соответствующий метод ввода в Presenter.

Что касается объявления протоколов, то я практикую TDD в стиле фиктивных ролей, а не объектов (http://www.jmock.org/oopsla2004.pdf). Протоколы помогают обеспечить лучшую абстракцию, сосредотачиваясь на том, что делает объект (его роль), а не на том, как он это делает.

Протоколы сами по себе не имеют большого значения для модульных тестов. Ваши модульные тесты будут предоставлять тестовые двойники (http://martinfowler.com/bliki/TestDouble.html) для зависимостей тестируемой системы. Даже если вы предоставляете зависимости как конкретные классы, вы все равно можете создавать тестовые двойники для своего теста.

В Objective-C можно использовать имитирующую библиотеку, например OCMock (http://ocmock.org), или OCMockito (https://github.com/jonreid/OCMockito), для создания заглушек, шпионов или макеты конкретного класса.

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

Короче говоря, протоколы используются не для облегчения модульного тестирования, а для описания на более высоком уровне абстракции того, что делает приложение.

Вот пример того, как абстрактные протоколы были полезны постфактум:

Я создал протокол для представления действий, которые пользователь может выполнять на экране, например. ProfileUserActions с такими действиями, как changeName и changeAddress. Презентатор реализовал ProfileUserActions, а представление приняло ProfileUserActions в качестве зависимости. Когда пользователь нажимал кнопку на экране, представление отправляло соответствующее сообщение своему объекту userActions.

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

person Jeff Gilbert    schedule 18.12.2016
comment
Спасибо за ваш подробный ответ. Я получил разрешение с целью протоколов. Я все еще немного запутался с UNIT Testing :) извините за это. Может быть, пример кода может дать мне лучшую картину - person MaK; 21.12.2016
comment
Попался! Извините за это :-) Когда вы пишете свои модульные тесты? Прежде чем писать код, например. ТДД? Или после написания кода? - person Jeff Gilbert; 21.12.2016
comment
На самом деле мне нужно написать модульный тест для существующей базы кода, построенной по модели viper - Планирую использовать BDD. И я фактически разместил еще один вопрос о TDD против BDD :) stackoverflow.com/questions/41264111/tdd-vs-bdd-rest-service - person MaK; 21.12.2016
comment
Я отредактировал свой ответ, чтобы описать значение протоколов в модульных тестах. - person Jeff Gilbert; 21.12.2016

Используя протокол, вам легче заменить реализацию в вашей структуре VIPER. Например, у вас может быть интерактор, работающий с классом, выполняющим запись в файловую систему. Вы не хотите тестировать файловую систему в своих модульных тестах, поэтому вместо этого, если вы поместите операции записи файловой системы в свой интерактор за протоколом, вы можете затем заменить операции записи файловой системы реализацией в памяти.

Что касается протокола самого интерактора, я думаю, стоит быть немного более прагматичным в своем выборе. Если создать свой интерактор для теста несложно и он не вызывает побочных эффектов как часть тестирования, то, вероятно, в протоколе нет необходимости. С другой стороны, если вам нужно создать ряд других зависимостей, то может быть выгодно, чтобы интерактор соответствовал протоколу, чтобы вам было легче подделывать вывод, который вы получаете от интерактора.

person Community    schedule 17.12.2016