Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
182 lines (140 sloc) 20.3 KB

Онлайн стажировка Spring 5/JPA Enterprise (Topjava)

REST, REST контроллеры, тестирование Spring MVC контроллеров (несколько видео открыто)

Формат обучения на стажировке

  • Не стоит стремиться прочитать все ссылки урока, их можно использовать как справочник. Гораздо важнее пройти основной материал урока и сделать Домашнее Задание
  • Обязательно посмотри правила работы с патчами на проекте
  • Делать Apply Patch лучше по одному, непосредственно перед видео на эту тему, а при просмотре видео сразу отслеживать все изменения кода проекта по изменению в патче (Version Control->Local Changes-> Ctrl+D)
  • При первом Apply удобнее выбрать имя локального ченджлиста Name: Default. Далее все остальные патчи также будут в него попадать.
  • Код проекта обновляется и не всегда совпадает с видео (можно увидеть как развивался проект). Изменения в проекте указываю после соответствующего патча.

hw Разбор домашнего задания HW6

video 1. HW6

Apply 7_01_HW6_fix_tests.patch

  • Добавил AbstractServiceTest.isJpaBased() и Assume.assumeTrue(isJpaBased()) в AbstractMealServiceTest.testValidation().
  • Как вариант можно было вместо наследования от AbstractJpaUserServiceTest сделать @Autowired(required = false) JpaUtil (в этом случае, если в профиле jpa/datajpa не объявить JpaUtil, в тестах будет NPE)
  • В новой версии Spring классы spring-mvc требуют WebApplicationContext, поэтому поправил inmemory.xml

Apply 7_02_HW6_meals.patch

При переходе на AJAX JspMealController удалим за ненадобностью, возвращение всей еды meals() останется в RootController.

Apply 7_03_HW6_fix_relative_url_utf8.patch

video 2. HW6 Optional

Apply 7_04_HW6_optional_add_role.patch

JdbcUserServiceTest отвалились. Будем чинить в 7_06_HW6_optional_jdbc.patch

Apply 7_05_fix_hint_graph.patch

  • В JpaUserRepositoryImpl.getByEmail DISTINCT попадает в запрос, хотя он там не нужен. Это просто указание Hibernate не дублировать данные. Для оптимизации можно указать Hibernate делать запрос без distinct: 15.16.2. Using DISTINCT with entity queries
  • Тест DataJpaUserServiceTest.testGetWithMeals() не работает для admin (у админа 2 роли и еда при JOIN дублируется). DISTINCT при несколький JOIN не помогает. Оставил в графе только meals. Корректно поставить тип LOAD, чтобы остальные ассоциации доставались по стратегии модели. Однако с типом по умолчанию FETCH роли также достаются (похоже что бага).

Apply 7_06_HW6_optional_jdbc.patch

  • реализовал в JdbcUserRepositoryImpl.getAll() доставание ролей через лямбду
  • в insertRoles поменял метод batchUpdate и сделал проверку на empty

Еще интересные JDBC реализации:

  • в getAll()/ get()/ getByEmail() делать запросы с LEFT JOIN и сделать реализацию ResultSetExtractor
  • подключить зависимость spring-data-jdbc-core. Там есть готовый OneToManyResultSetExtractor. Можно посмотреть, как он реализован.
  • реализация, зависимая от БД (для postgres): доставать агрегированные роли и делать им split(","):
...

Занятие 7:

video 3. Тестирование Spring MVC

Apply 7_07_controller_test.patch

  • в MockMvc добавился CharacterEncodingFilter
  • добавил AllActiveProfileResolver
  • реализация Ehcache нетранзакционная, после отката транзакции в тестах по @Transactional Hibernate кэш не восстанавливал роль для USER. Добавил очистку кэша Hibernate в AbstractControllerTest.setUp (с учетом того, что Profiles.REPOSITORY_IMPLEMENTATION можно переключить на JDBC).

video 4. Миграция на JUnit 5

Apply 7_08_JUnit5.patch

No need junit-platform-surefire-provider dependency in maven-surefire-plugin

video 5. Принципы REST. REST контроллеры

Apply 7_09_rest_controller.patch

video 6. Тестирование REST контроллеров. Jackson

Apply 7_10_rest_test_jackson.patch

video 7. Кастомизация Jackson Object Mapper

Apply 7_11_jackson_object_mapper.patch

video 8. Тестирование REST контроллеров через JSONassert

Apply 7_12_json_assert_tests.patch

video 9. Тестирование через SoapUi. UTF-8

Apply 7_13_soapui_utf8_converter.patch

Импортировать проект в SoapUi из config\Topjava-soapui-project.xml. Response смотреть в формате JSON.

Проверка UTF-8: http://localhost:8080/topjava/rest/profile/text

ResponseBody and UTF-8

question Ваши вопросы

При выполнении тестов через MockMvc никаких изменений на базе не видно, почему оно не сохраняет?

AbstractControllerTest аннотируется @Transactional - это означает, что тесты идут в транзакции и после каждого теста JUnit делает rollback базы.

Что получается в результате выполнения запроса SELECT DISTINCT(u) FROM User u LEFT JOIN FETCH u.roles ORDER BY u.name, u.email? В чем разница в SQL без DISTINCT.

Запросы SQL можно посмотреть в логах. Те DISTINCT в JPQL влияет на то, как Hibernate обрабатывает дублирующие записи (с DISTINCT их исключает). Результат можно посмотреть в тестах или приложении, поставив брекпойнт. По поводу SQL DISTINCT не стесняйтесь пользоваться google, например оператор SQL DISTINCT

В чем заключается расширение функциональности hamcrest в нашем тесте, что нам пришлось его отдельно от JUnit прописывать?

hamcrest-all используется в проверках RootControllerTest: org.hamcrest.Matchers.*

Jackson мы просто подключаем в помнике и спринг будет с ним работать без любых других настроек?

Да, Spring смотрит в classpath и, если видит там Jackson, то подключает интеграцию с ним

Где-то слышал, что любой ресурс по REST должен однозначно идентифицироваться через url, без параметров. Правильно ли задавать URL для фильтрации в виде http://localhost/topjava/rest/meals/filter/{startDate}/{startTime}/{endDate}/{endTime} ?

Так делают, только при отношении агрегация. В случае критериев, поиска или страничных данных они передаются как параметр. Смотри также:

Что означает конструкция в JsonUtil: reader.<T>readValues(json);

См. Generic Methods. Когда компилятор не может вывести тип, можно его уточнить при вызове generic метода. Не важно static или нет.

hw Домашнее задание HW07

  • 1: Добавить тесты контроллеров:
    • 1.1 RootControllerTest.testMeals для meals.jsp
    • 1.2 ResourceControllerTest для style.css (проверить status и ContentType)
  • 2: Реализовать MealRestController и протестировать его через MealRestControllerTest
    • 2.1 cледите чтобы url в тестах совпадал с параметрами в методе контроллера. Можно добавить логирование <logger name="org.springframework.web" level="debug"/> для проверки маршрутизации.
    • 2.2 в параметрах getBetween принимать LocalDateTime (конвертировать через @DATETIMEFORMAT WITH JAVA 8 DATE-TIME API), а передавать в тестах в формате ISO_LOCAL_DATE_TIME (например '2011-12-03T10:15:30'). Вызывать super.getBetween() пока без проверки на null, используя toLocalDate()/toLocalTime() (см. Optional п.3)

Optional

  • 3: Переделать MealRestController.getBetween на параметры LocalDate/LocalTime c раздельной фильтрацией по времени/дате, работающий при null значениях (см. демо и JspMealController.getBetween). Заменить @DateTimeFormat на свои LocalDate/LocalTime конверторы или форматтеры.
  • 4: Протестировать MealRestController (SoapUi, curl, IDEA Test RESTful Web Service, Postman). Запросы curl занести в отдельный md файл (либо README.md)

На следующем занятии используется JavaScript/jQuery. Если у вас там пробелы, пройдите его основы


error Подсказки по HW07

  • 1: Ошибка в тесте Invalid read array from JSON обычно расшифровывается немного ниже: читайте внимательно.
  • 2: Jackson и неизменяемые объекты
  • 3: Jackson JSON Tutorial
  • 4: Если у meal, приходящий в контроллер, поля null, проверьте @RequestBody перед параметром (данные приходят в формате JSON)
  • 5: При проблемах с собственным форматтером убедитесь, что в конфигурации <mvc:annotation-driven... не дублируется
  • 6: Проверьте выполение ВСЕХ тестов через maven. В случае проблем проверьте, что не портите константу из MealTestData
  • 7: @Autowired в тестах нужно делать в том месте, где класс будет использоваться. Общий принцип: не размазывать код по классам, объявление переменных держать как можно ближе к ее использованию, группировать (не смешивать) код с разной функциональностью.
  • 8: Попробуйте в RootControllerTest.testMeals сделать сравнение через model().attribute("meals", expectedValue). Учтите, что вывод результатов через toString к сравнению отношения не имеет
  • 9: Посмотрите, нет ли в MealTestData методов, которые можно сделать общими (через generic и TestUtil)
You can’t perform that action at this time.