-
Notifications
You must be signed in to change notification settings - Fork 35
Lesson 7
Онлайн проекта Topjava
Инициализировал пустые роли юзера через
EnumSet.noneOfдля возможности сделатьadd
1. HW6
- Добавил
AbstractServiceTest.isJpaBased()иAssume.assumeTrue(isJpaBased())вAbstractMealServiceTest.testValidation().- Как вариант можно было вместо наследования от
AbstractJpaUserServiceTestсделать@Autowired(required = false) JpaUtil(в этом случае, если в профиле jpa/datajpa не объявитьJpaUtil, в тестах будет NPE)- В новой версии Spring классы
spring-mvcтребуютWebApplicationContext, поэтому поправилinmemory.xml
При переходе на AJAX JspMealController удалим за ненадобностью, возвращение всей еды meals() останется в RootController.
2. HW6 Optional
JdbcUserServiceTest отвалились. Будем чинить в 7_06_HW6_optional_jdbc.patch
- В
JpaUserRepositoryImpl.getByEmailDISTINCT попадает в запрос, хотя он там не нужен. Это просто указание Hibernate не дублировать данные. Для оптимизации можно указать Hibernate делать запрос без distinct: 15.16.2. Using DISTINCT with entity queries - Тест
DataJpaUserServiceTest.testGetWithMeals()не работает для admin (у админа 2 роли и еда при JOIN дублируется).DISTINCTпри несколький JOIN не помогает. Оставил в графе толькоmeals. Корректно поставить типLOAD, чтобы остальные ассоциации доставались по стратегии модели. Однако с типом по умолчаниюFETCHроли также достаются (похоже что бага).
- реализовал в
JdbcUserRepositoryImpl.getAll()доставание ролей через лямбду- в
insertRolesпоменял методbatchUpdateи сделал проверку на empty
Еще интересные JDBC реализации:
- в
getAll()/ get()/ getByEmail()делать запросы сLEFT JOINи сделать реализациюResultSetExtractor - подключить зависимость
spring-data-jdbc-core. Там есть готовыйOneToManyResultSetExtractor. Можно посмотреть, как он реализован. - реализация, зависимая от БД (для postgres): доставать агрегированные роли и делать им
split(","):
SELECT u.*, string_agg(r.role, ',') AS roles
FROM users u
JOIN user_roles r ON u.id=r.user_id
GROUP BY u.id
- в
MockMvcдобавилсяCharacterEncodingFilter- добавил
AllActiveProfileResolver- реализация Ehcache нетранзакционная, после отката транзакции в тестах по
@TransactionalHibernate кэш не восстанавливал роль для USER. Добавил очистку кэша Hibernate вAbstractControllerTest.setUp(с учетом того, чтоProfiles.REPOSITORY_IMPLEMENTATIONможно переключить наJDBC).
No need
junit-platform-surefire-providerdependency inmaven-surefire-plugin
- JUnit 5 homepage
- Overview
- 10 интересных нововведений
- Дополнительно:
- JSON (JavaScript Object Notation
- Understanding REST
- 15 тривиальных фактов о правильной работе с протоколом HTTP
- 10 Best Practices for Better RESTful
- Request mapping
- Дополнительно:
- Сериализация hibernate lazy-loading с помощью jackson-datatype-hibernate
- Handle Java 8 dates with Jackson
- Дополнительно:
- Инструменты тестирования REST:
- SoapUi
- Написание HTTP-запросов с помощью Curl (для Windows можно использовать Git Bash). Для работы с UTF-8 в Windows 10 нужны пляски с бубном: "Язык и региональные стандарты" -> "Сопутствующие параметры" -> "Административные языковые параметры" -> "Изменить язык системы" -> галка "Бета-версия:Использовать Юникод (UTF-8) для поддержки языка во всем мире", перезагрузка.
- Postman
- IDEA: Tools->HTTP Client->...
- Insomnia REST client
Импортировать проект в SoapUi из config\Topjava-soapui-project.xml. Response смотреть в формате JSON.
Проверка UTF-8: http://localhost:8080/topjava/rest/profile/text
При выполнении тестов через 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}?
Так делают, только при отношении агрегация, например если давать админу право смотреть еду любого юзера, URL мог бы быть похож на http://localhost/topjava/rest/users/{userId}/meals/{mealId}. В случае критериев, поиска или страничных данных они передаются как параметр. Смотри также:
Что означает конструкция в
JsonUtil:reader.<T>readValues(json);
См. Generic Methods. Когда компилятор не может вывести тип, можно его уточнить при вызове generic метода. Не важно static или нет.
- 1: Добавить тесты контроллеров:
- 1.1
RootControllerTest.testMealsдляmeals.jsp - 1.2
ResourceControllerTestдляstyle.css(проверить status и ContentType)
- 1.1
- 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)
- 2.1 cледите чтобы url в тестах совпадал с параметрами в методе контроллера. Можно добавить логирование
- 3: Переделать
MealRestController.getBetweenна параметрыLocalDate/LocalTimec раздельной фильтрацией по времени/дате, работающий приnullзначениях (см. демо иJspMealController.getBetween). Заменить@DateTimeFormatна свои LocalDate/LocalTime конверторы или форматтеры. - 4: Протестировать
MealRestController(SoapUi, curl, IDEA Test RESTful Web Service, Postman). Запросыcurlзанести в отдельныйmdфайл (либоREADME.md)
На следующем занятии используется JavaScript/jQuery. Если у вас там пробелы, пройдите его основы
- 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)
Правка
Разбор домашнего задания HW6
Ваши вопросы