Если нельзя, но очень хочется, то нужно обязательно и ничего в мире не стоит того, чтобы делать из этого проблему!


Интересна Java? Кликай по ссылке и изучай!
Если тебе полезно что-то из того, чем я делюсь в своем блоге - можешь поделиться своими деньгами со мной.
с пожеланием
столько времени читатели провели на блоге - 
сейчас онлайн - 

вторник, 28 февраля 2012 г.

Java for fun: #4 Пишем Web проект на Java в Eclipse для Tomcat. Билдим Ant. Проверяем Hudson. Тестим jUnit + EasyMock + jWebUnit. Коммитим в Svn.

В первой части я расказал как настроить самое основное окружение. Во второй части мы начали делать login page и почти закончили ее.  В третьей части мы еще что-то делали, ах да! сделали User сервис таким, чтобы он мог работать с простыми файлами. 

Сейчас мы хотим, написать unit-тесты для Exam контроллера. Читать дальше (там много буков)...

Итак первый тест. И почему-то нерабочий. Странно...


Но это всего лишь моя очередная ошибка, исправив которую можно добиться работоспособности теста-требования...


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

Итак тест с учетом последних нововведений будет скорее уже не тест а требование, которому пока еще система не соответствует. Я разбил тест на несколько условных частей: 1 - контроллером проверяется режим 2 - из сесии выбирается билет пользователя, но его там нет. Так же выбирается имя самого пользователя. 3 -так как нет билета то он запрашивается у экзаменатора для конкретного студента 4 - некоторые константы 5 - ожидается заданный вопрос, варианты ответа и названия экзамена. 6 - они все уходят в риквест, чтобы потом отобразиться на страничку jsp 7 - переход на соответствующую страничку jsp 8 - запуск теста...


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


У нас пока нет Экзаменатора - а значит создаем этот интерфейс...




Так же поступаем и с билетом - это еще один интерфейс...




Так же нам надо создать метод выдачи билета в интерфейсе...


По умолчанию как result type выставляется Object, но мя его изменим на интерфейс билета....


Так же создадим методы для работы с билетами...



И подобным образом для всех остальных...


Запустив тест я нарываюсь на null pointer exception, а все потому что я не за'mock'ал интерфейс Session, которым пользуюсь....


Немного изменяем тест...


Добавляем новый мок...


Запускаем тест-требование и видим несоответствия системы...


Чтобы тест заработал я думаю сделать в контроллере следующее...


Настраиваем экзаменатора для контроллера...


Его пока нет, а потому создаем...




Запуск теста-требования не успешный. Почему?...


Идем в гугл за ответом. На самом деле довольно часто в той области, в которой мало опыта программист отправляется за ответом в google - такая разработка называется google driven development...


После легкого перерыва ответ пришел сам - я забыл зарегистрировать новые моки...


Теперь null pointer возникает по другой причине - где-то что-то недореализовано...


Вот оно!... Только вопрос, почему дернулась реальная реализация а не мок?...


Потому что контроллер не сконфигурирован для работы с разными экзаменаторами (в нашем случае с моковой реализацией)...


Вот так вставляем в контроллера нашего экзаменатора...


Еще одна ошибка с моей стороны - это не та страничка в тесте...


тест прошел!


Но поломались другие....

Все потому, что не реализованы внутренности экзаменатора и билета...


Класса нет - создаем...




Заменяем null на ожидаемые конастанты и все работает!...


Вот какая структура проекта сейчас...


Еще один тест-требование для работы с страничкой результатов...


Метода нет - создаем...



В реализацию добавляем...


Требование готово! Система пока не соответствует ему...


Делаем изменения в системе...


Требование-тест говорит нам, что мы что-то забыли...


А еще оказалось, что я допустил ошибку в требовании...


Удавил эту лишнюю строчку мы добиваемся зеленой полосы...


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


Нам надо обрабатывать заполненную форму с ответом на вопрос. Раньше это делалось в doResult а теперь это логичнее оставить в processQuestion...



В этом методе мы возьмем билет из сессии и скопируем ответ в него из формы jsp странички...


Метода пока нет, но мы сделаем...



Так же надо исправить требование в соответсвии с изменениями в системе...


Реализуем все для успешной компиляции...



Причина ошибки поменялась, и пока не ясно что сейчас не работает...


Оказывается старые требования не исправлены (мы меняли jsp страничку но не поменяли требования проверяющие ее работу)...


Снова неожиданная ошибка!...



без дебаггера никак не получается обойтись. Ставим точку останова и запускаем тест в режиме дебага...


Как оказалось билета в этом месте в сессии нет. Почему?...


А все потому что после получения билета от экзаменатора я не поставил его в сессию (хотя такое в планах было).

Требование все так же не работает но по причине нереализованной функции экзаменатора...


Ее и реализуем...


Метода нет - создадим...





Кажется тут уже требование не соответствует изменениям в системе...


Исправляем требование...


И все работает!...


Теперь я хочу навести порядок в новой подборке требований которую мы только что написали. Я буду пользоваться такими типами рефакторинга: Extract Method, Inline, Extract Local Variable. По сути я буду устранять существующее дублирование в коде и повышать читабельность...

Смотрите внимательно что делаю...







И так же для остальных методов-тестов. Основная идея такая: находим строки, которые отличаются всего лишь константами - делаем для всех констант Extract Local Variable после чего исходные строки будут уже идентичны - одной из них делаем Extract Method и IDE автоматически выделит метод и устранит дублирование во всех местах. После всего этого мы избавляемся от локальным переменных с помощью Inline - они нам более не нужны. Это все можно было сделать руками, но что делается руками человека то скорее всего содержит ошибку - я не хотел внести дополнительную ошибку.

То же самое проделаем с другой строчкой...


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


Запоминаем горячие клавиши - с ними работается быстрее...





Вот так выглядят треьбования после рефакторинга - как по мне, то значительно читабельнее...


И главное все работает...


Теперь можно и сохраниться...



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

Комментариев нет:

Отправить комментарий