Привет. Вот ссылка на прошлую серию. Вот краткий план на эту часть:
= для начала мы добавим валидацию входных данных для 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
А что если мы его поудаляем?
Тогда нам понадобятся соответствующие поля в классе контроллера или, что еще лучше, в его родителе
И вместе с тем, я собираюсь не делать перехода в наследниках, а сделать его в родителе. Почему так? Потому что любой doAction последней своей строчкой осуществляет переход, а это больше похоже на абстрактное поведение родителя Controller.При этом метод doAction будет возвращать то место, куда стоит перейти.
Вот как изменится при этом абстрактный контроллер.
И как упростится контроллер логинки
Естественно то же самое проделать и с другими контроллерами
Вуаля!
И контроллер админки
Ай красота!
С тестами не так все красиво
Ну еще бы, мы теперь всегда получаем сессию сразу, а потом делаем необходимые действия с сылкой на нее, а раньше мы пользовались риквестом для получения сессии только тогда, когда в этом была необходимость
Запрограммируем в initMocks это новое поведение. Вместе с тем, все getSession() стоит удалить.
Картина улучшилась, но не идеальна
Поищем-ка все места, в которых возможно остались вызовы getSession()
Тут все нормально - получаем один раз и пользуемся когда надо
Это лишнее
Тут программирование мока на единоразовое получение сессии
Это тоже лишнее
Вот теперь идеально!
Коммитимся. Я доволен проделанной работой!
После перерыва запускаю все тесты
Есть одна бага, которую я хотел бы повфиксить. А именно что-то где-то внутри работает так, что если я создам нового пользователя, то вся информация о проденных ранее экзаменах и их пользователях удалится.
Пишем два теста функциональный
и тест для userService
Немного подумав могу объяснить почему так происходит: При добавлении нового пользователя происходит обычная загрузка списка, при которой игнорируются все юзера, которые уже прошли тест, а после добавления нового пользователя этот отфильтрованный список сохраняется.
Посмотрим, где еще используется загрузка списка пользователей с отфильтровыванием отстрелявшихся
Анализ результатов натолкнул на мысль, что подобная ошибка будет встречаться и при прохождении юзером экзамена. Следующий тест тому доказательство
Выход простой - удалить фильтр из метода загрузки пользователей.
Я в тесте немного ошибся написав SUCESS вместо PASSED
Исправление помогло
Но осталось еще два теста. Это потому, что мы удалили фильтр а он нам нужен
А потому мы добавим фильтрацию в то место, где она нужна была, чем исправляем оставшиеся тесты
При этом я заметил, что загрузка списка файлов в конструкторе не имеет никакого смысла, а потому я удалю эту строчку
Теперь обратимся к истории запуска тестов и выберем тот, который осталось пофиксить.
Запустим еще раз
И исправим
Теперь все работает
Теперь я хочу устрнаить дублирование из двух новых тестов
Дублирование устранено и тесты зеленые
Все тесты так жезеленые
А потому коммитимся
Продолжение следует...
= для начала мы добавим валидацию входных данных для 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
Исправление помогло
Но осталось еще два теста. Это потому, что мы удалили фильтр а он нам нужен
А потому мы добавим фильтрацию в то место, где она нужна была, чем исправляем оставшиеся тесты
При этом я заметил, что загрузка списка файлов в конструкторе не имеет никакого смысла, а потому я удалю эту строчку
Запустим еще раз
И исправим
Теперь все работает
Теперь я хочу устрнаить дублирование из двух новых тестов
Дублирование устранено и тесты зеленые
Все тесты так жезеленые
А потому коммитимся
Продолжение следует...
Комментариев нет:
Отправить комментарий