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


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

суббота, 19 января 2013 г.

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

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

Начнем, как всегда, с запуска всех тестов - мало ли, может кто-то, пока мы спали, что-то поломал?


Итак проверка нестандартных входных данных для UserService. Вот список того, что сразу пришло в голову. Я ее тут же очистил от идей, чтобы было место для других мыслей.


Два новых теста, и оба не работают.


Одна простая валидация и все заработало


Еще один новый нерабочий тест


Еще одна проверка


Еще 4 новых нерабочих теста


Первая проверка удачно исправила один из тестов


Вторая не такая удачная - где-то ошибочка


В тесте ошибка, исправляем ошибку - тест зеленый


Еще две проверки - одна успешна, другая не совсем


Снова в тесте неточность. Исправили - все зеленое.


Все тесты?


Вот интерфейс пользователя, давай подумаем,как еще его можно поломать?


Вот такие идеи у меня пришли в голову, а потому я их запишу


И реализую первых 4 из них


Видимо эта валидация мне понадобится в другом методе, потому я ее выделяю.


Помогло!


Все тесты так же зеленые.


Еще три проверки


Еще немного валидации в коде


Еще две проверки


Еще валидация


Привел немного интерфейс в порядок, а то имена отличались


Все тесты - зеленые


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


Логинюсь под пустым именем


И вижу этот ужас! Кирилица, блин, не правильно выводится. И кроме всего видно весь stack trace. Фуу...


Обернем в try/catch все тело doPost контроллера логинки


Все, что бы не произошло (любой Exception) будет направлено на страничку error.jsp. Там сообщение об исклюючительной ситуации выведется на экран.


Страничку надо создать.


И оформить внутри


Теперь можно попробовать запустить приложение 


И сделать то, же что и в прошлый раз


Сообщение вывелось, но кириллица не отобразилась.


Ничего - потом с ней разберемся. А сейчас у меня появилась одна суперская идея по рефакторингу! Мы выделим родителя для всех контроллеров.


Тело его будет очень похожим на тело LoginController, с той лишь разницей, что не будет объявлен метод doAction - реализацию этого метода мы оставим на наследников, ибо логика LoginController отличается от логики ExamController и AdminController. А все их сходство находится в этом абстрактном родительском классе. Данный подход называется шаблоном Template Method или Шаблонным методом.


А вот как изменится наш LoginController, если мы его сделаем наследником.


Я больше не хочу запускать браузер и тестить ручками. Если первый раз это было прикольно, то второй раз нудно, а третий будет невыносимо, а потому я напишу функциональный тест.


Вот наша локализация и вылезла. У меня идея простая - заменить все сообщения на english, потому как весь интерфейс приложения изначально не русский.


Естественно послетали тесты


Я решил выделить сообщения в константы, с тем, чтобы потом пришлось меньше править


И заменил в тестах текст на использование констант


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


Потому написал этот же тест для userService


И исправил порядок


То же я сделал и для другого методы addUSer


И исправил порядок


Функциональный тест так же исправился


Теперь я хочу сделать подобный функциональный тест для админки - там тоже неприемлемо добавление пользователя с пустым именем или паролем. Но для начала я сделаю видимым один необходимый мне метод


И напишу новый тест


Метода addUser пока еще нет, но мы уже имеем для него заготовку 


Сделаем из нее метод


Тест валится с 500 ошибкой. Ну и неудивительно - я try/catch добавил только в контроллер логинки.


А потому применим наследование от Controller и для контролера админки


Все тесты прошли! Супер!


То же сделаем и для ExamController


Люблю удалять повторяющийся код

Но если запустить все тесты, то увидим проблемку. То место, где мы использовали java reflection для вызова метода doPost контроллера, явно не готово было к нашим изменениям.


Какая шумная ошибка и как просто исправилась


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


Вполне можно удалить, переместив в абстрактного родителя


Вот так


Вот оно! Помнишь я говорил, что не стоит беспокоиться о временном размещении и модификаторах метода isUserLoggedOut/ Теперь настал его час - я перемещаю его в Controller


Теперь он не static и не public


Много тестов не работает!


А все потому, что проверять залогинен ли пользователь уместно только, если мы не на логинке. Проверим этот факт - и снова все зеленое!


Посмотрим теперь на три наших контроллера и поищем дублирование. Оно точно тут есть!




Да хотя бы вот.


Введем локальную переменную и осуществим forward один раз


Локальная переменная мне не очень нравится, но пока мы это проигнорируем...

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



Ну что? Вроде как тоже ничего и никаких локальных переменных.


Теперь посмотрим еще раз внимательно. Где тут дублирование?


А повсюду передается request и responce


А что если мы его поудаляем?


Тогда нам понадобятся соответствующие поля в классе контроллера или, что еще лучше, в его родителе


 В связи с новыми полями, можно еще упростить код


А что, если так же добавить и сессию, как поле класса? 


Тогда  можно еще упростить контроллер логинки. Так же хочу обратить внимание, что метод goTo более абстрактный чем конкретная реализация doAction, а потому ему больше место в классе Controller нежели тут


И вместе с тем, я собираюсь не делать перехода в наследниках, а сделать его в родителе. Почему так? Потому что любой doAction последней своей строчкой осуществляет переход, а это больше похоже на абстрактное поведение родителя Controller.При этом метод doAction будет возвращать то место, куда стоит перейти.


Вот как изменится при этом абстрактный контроллер.


И как упростится контроллер логинки


Естественно то же самое проделать и с другими контроллерами


Вуаля!


И контроллер админки


Ай красота!


С тестами не так все красиво


Ну еще бы, мы теперь всегда получаем сессию сразу, а потом делаем необходимые действия с сылкой на нее, а раньше мы пользовались риквестом для получения сессии только тогда, когда в этом была необходимость
Запрограммируем в initMocks это новое поведение. Вместе с тем, все getSession() стоит удалить.




Картина улучшилась, но не идеальна


Поищем-ка все места, в которых возможно остались вызовы getSession()


Тут все нормально - получаем один раз и пользуемся когда надо


Это лишнее


Тут программирование мока на единоразовое получение сессии


Это тоже лишнее


Вот теперь идеально!


Коммитимся. Я доволен проделанной работой!



После перерыва запускаю все тесты


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

Пишем два теста функциональный


и тест для userService


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


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


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


Выход простой - удалить фильтр из метода загрузки пользователей.


Я в тесте немного ошибся написав SUCESS вместо PASSED


Исправление помогло


Но осталось еще два теста. Это потому, что мы удалили фильтр а он нам нужен


А потому мы добавим фильтрацию в то место, где она нужна была, чем исправляем оставшиеся тесты


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


Теперь обратимся к истории запуска тестов и выберем тот, который осталось пофиксить.


Запустим еще раз


И исправим


Теперь все работает


Теперь я хочу устрнаить дублирование из двух новых тестов



Дублирование устранено и тесты зеленые



Все тесты так жезеленые


А потому коммитимся


Продолжение следует...

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

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