Модульное тестирование контроллеров

whirlwind

TDD infected, paranoid
Модульное тестирование контроллеров

Давно пытаюсь подобрать методику для тестирования контроллеров, но что-то она никак не вырабатывается. Сложность тестирования контроллеров заключается в том, что контроллеры работают с разными уровнями приложения, например модель, контекст запроса, апи какого-либо сервиса. Это влечет за собой и сложную фикстуру, которая не укладывается в привычные 5-10 строчек, и много ассертов. Приходится либо городить настройки фикстуры и кучу ассертов в методе теста, либо расширять тесткейс дополнительными ассертами и методами настройки фикстуры.

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

Ну и что бы не обсуждать сферического коня в вакууме, приведу примеры

Контроллер: http://pastebin.com/SjXcGck9
Тест: http://pastebin.com/fWNHca1W

Я вижу где можно поработать над методом FE_Register::form для сокращения кода и фикстуры теста, например скрыть некоторые запросы к api за фасадом. Но не вижу каким образом можно изменить ситуацию кардинально. Ведь там и так только переходы по состояниям if/else.

Кто как тестирует контроллеры?
 

korchasa

LIMB infected
Я предпочитаю уменьшать код контроллера. Меньше кода = меньше тестов. Мало кода и он тупой = нафиг мне его тестировать.
 

whirlwind

TDD infected, paranoid
Мы тоже так пытались. Работа под лозунгом "перенеси это куданибудь" у нас привела к размазыванию логики по разным классам, оверхеду по фикстуре, излишней декомпозиции (вложенные контроллеры). Если есть функциональное требование, то куда код не переноси - все равно где то его спецификация в тесте будет. Тут вопрос скорее в другом, как лучше организовать контроллеры, что бы их было легче тестировать? Может что то по типу КА, который поддается формализации легче, чем тестируется такой вот контроллер как сейчас. И тестировать по отдельности переходы. Не пробовали?
 

fixxxer

К.О.
Партнер клуба
Я стараюсь, чтобы контроллеры помещались на экран, а весь подобный код выносить в отдельные сущности (или их еще сервисами называют). В итоге контроллер вообще не нуждается в тестировании - он туп как пробка, и состоит только из создания моделей/сервисов и их привязывания к вьюхе (ну да, еще в случае приема POST-а 4-5 строк на вызов соответствующего действия и редирект при успехе).

Сами сущности/сервисы при этом прекрасно тестируются идентично моделям. И да, в простых случаях сущность сама выполняет и роль модели (ну точнее все сводится к классическому mvc ;).
 

korchasa

LIMB infected
fixxxer
+1 Я сервисы и хэлперы и имел ввиду.

whirlwind
Может вы сущности выделяли не так? Они по идее не должны знать о запросе, шаблонах и т.д. Ввод-вывод в контроллере, логика процесса в сервисе.
 

whirlwind

TDD infected, paranoid
korchasa
Ты по поводу контроллеров? Там у нас было типа выделение состояний в классы. А вышестоящий контроллер тестировал на моках правильность переходов.



Понятно. Ну вы примеры поглядели? Он не 10 строк но сложности именно с такими ветвистыми получаются. Там где на стыке разных сервисов надо анализировать результат и юзеру выдавать. В остальных случаях прекрасно работает Tell, Dont Ask. Все что касается модели там или сервисов, с этим унас никаких проблем нет. Сложности именно с контроллерами. Представьте что делает контроллер оформления заказа и платежа. Вот примерно такой сложности. Снаружи они должны выглядеть как транзакция.
 

fixxxer

К.О.
Партнер клуба
Если твой пример смотреть? Ну тут как раз напрашивается дополнительный сервисный слой. Я, конечно, не все понял, но тут явно речь идет о какой-то среде, увязывающей несколько сущностей - как раз напрашивается такая "управлялка" отдельным классом.
 

whirlwind

TDD infected, paranoid
А какой слой? Задача этого контроллера - работать с 3мя источниками данных: модель, реквест, и сторонний апи. Что из этих трех куда можно спокойно перенести? Если совать реквест в модель или апи в модель или еще куда то это повысит коуплинг разных абсолютно не связанных слоев. Хотя, теоретически апи можно сделать под класс модели, но сути это не поменяет. Даже способ реагирования системы на ошибки в этих трех источниках различаются это вроде как очевидно. Для реквеста валидация и пользователь правит. У модели свое, эксепшены или события например. А у апи коды ответов, которые надо пользователю показывать так как апи - это сущность неподконтрольная. Все задействованные сущности не одного порядка.

Пообщался тут, выясняется что у многих КА-подобное воркфлоу на паттернах: command, chain, decorator.
 

Духовность™

Продвинутый новичок
любители плодить методы, которые используются лишь в одном месте будут гореть в аду
 

HraKK

Мудак
Команда форума
whirlwind
Введи сервис Персонаж. И в него вынеси все методы работы с ним.
А в контролере только получай данные извне и отдавай обратно результаты работы сервиса Персонаж. Улучшишь читабельность кода, уменьшишь в разы тесты и добавишь реюзабельность.

Я думал ты уже давно перешел на этот уровень дзена.
 

whirlwind

TDD infected, paranoid
HraKK Service Layer ?

А где про него почитать норм можно? А то в гофе его нету, вот я и не знаю.
 

Духовность™

Продвинутый новичок
А где про него почитать норм можно?
http://ooad.asf.ru/Pattern.aspx?IdKat=7&IdPat=6

Я так понимаю (исходя из прошедших дискуссий на форуме) слой служб - это фактически логика конкретного приложения, содержащая в себе совокупность моделей, источников данных и кода, отвечающего за взаимодействие всего этого в рамках определенных, конкретных задач. Т.е. Service Layer - это основная логика приложения, выше которой в абстракции слоев стоит только контроллер, который абсолютно не должен что-то знать о логике приложения, а должен выполнять роль регулировщика.

Господа гуру, подскажите, я правильно понял сервисы?
У меня есть такой код контроллера: http://pastebin.com/sLKrG6Q5
Этот контроллер проверяет ссылку из письма и отсылает новый пароль. Генерация уникальной ссылки, отправка её на почту, проверка хэша их почты и отправка на почту нового пароля - все это делает сервис http://pastebin.com/cvrpq5mh
 

whirlwind

TDD infected, paranoid
понадпридумывают паттернов, без пузыря не разберешься %)
 

HraKK

Мудак
Команда форума
whirlwind
я не читал нигде никаких патернов, когда читаю говорю лишь "ах вот как это называется". Просто сам доходишь постепенно к удобству. Попробуй переписать все на Service Layer, я когда дошел до этой мысли переписал свою цмс на этом методе и подумал какой я был мудак что не додумался до этого изначально. И даже сейчас иногда делаю как ты, а потом когда надо реюзабилити бью себя челом об стол и понимаю что я заговнокодил опять и переписываю на на сервисы. Рульная штука.
 

whirlwind

TDD infected, paranoid
HraKK да я в основном по модели да апи специализируюсь . А тут чета столкнулся, припух. Щас полез читать SOA, офигел сколько там полезных штук всяких.

Всем спасибо за наводку.
 

Alexandre

PHPПенсионер
сорри за оффтоп
я конечно молчу в тряпочку и не могу тягаться с Титанами ООП
но прочтя Фаулера я бы функцию _form разбил бы на две-три части (использовал два-три приватных метода)
а так, код радует глаза

кстати год-два назад был интересный тред на предмет "тестирования приватных методов"

кстати на счет паттернов, ввидение еще одной абстрактной прослойки может успростить как понимание кода так и тестирование.
MVC - не догма а руководство к действию
 

whirlwind

TDD infected, paranoid
Alexandre не, Саш, мой код гавно. Разбивать на методы это тот же самый Ка делать который я выше упоминал. Это не так элегантно решает проблему как сервисный слой, учитывая что затык именно в тестировании. Просто у меня в голове не укладывалось как можно мешать разные слои. Нарыл "архитектуру корпоративных" сижу втыкаю.
 

korchasa

LIMB infected
Автор оригинала: whirlwind Просто у меня в голове не укладывалось как можно мешать разные слои.
Они не мешаются, просто вводится еще один, который забирает часть кода из моделей и контроллеров.
 
Сверху