Как да тествате услуги, крайни точки и хранилища в Spring Boot

В този пост ще ви покажа как да пишете модулни тестове в приложения за пролетно зареждане.

Защо е необходимо да се пише единичен тест изисква друга статия за обяснение. Но за кратко обяснение ще ви кажа няколко неща.

Обикновено защитавам аргумента, че кодът без модулни тестове е мъртъв код. Защото, когато разработчикът добави нова функция към някакъв код, който не е обхванат от единичен тест, той е склонен да отмени съществуващите бизнес правила (което убива написания преди код). Може би не е точно склонен към това, но можете да си представите какви грешки могат да възникнат, когато сложен проект трябва да бъде променен. Единичното тестване е единственият начин да защитите кода си от пробивни промени.

Защо крайни точки за единичен тест?

Всеки път, когато пишем крайна точка, трябва да сме сигурни, че няколко неща работят правилно. Крайната точка трябва да връща данните в правилната структура и да обработва заявката правилно. Можем да го тестваме ръчно, което не е за предпочитане. Затова пишем модулни тестове, за да гарантираме, че крайните ни точки работят правилно. Има и друг начин за тестване на крайни точки, известен като тестове за автоматизация, но това не е предмет на тази публикация.

Защо услугите за единични тестове?

Вече трябва да е ясно, но за всеки случай: трябва да сме сигурни, че нашата бизнес логика работи правилно.

Защо хранилища за единични тестове?

Има няколко случая за тестване на хранилища. Разбира се, ние не тестваме самата рамка. Но ние пишем модулни тестове, за да сме сигурни, че нашите спецификации или връзки са приложени правилно.

И така, как да тестваме контролери?

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

Забележка: Примерите, показани в тази публикация, не са за реална архитектура на производствената употреба

@[email protected] class User { @Id @GeneratedValue(generator = "uuid2") @GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator") @Column(name = "id", columnDefinition = "BINARY(16)") private UUID id; private String name; private String email; private int age;}
@Datapublic class CreateUserRequest { private String name; private String email; private int age;}
@[email protected]("/users")public class UserController { UserService userService; @Autowired public UserController(UserService userService) { this.userService = userService; } @PostMapping public ResponseEntity createUser(@RequestBody CreateUserRequest request) { User created = userService.save(request); return ResponseEntity.ok(created); }}

Нашият контролер има зависимост от UserService, но ние не се интересуваме от това, което услугата прави в момента.

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

Подигравахме се с нашата услуга, защото не се нуждаем от подробности за нейното изпълнение. Ние просто тестваме нашия контролер тук. Използваме MockMvcтук, за да тестваме нашия контролер и картографиране на обекти за целите на сериализацията.

Ние настройваме userService.Save() метода си да връща желания потребителски обект. Минахме искане към нашия контролер и след това ние проверихме върнатите данни с следния ред: andExpect(jsonPath("$.name").value(request.getName())).

Имаме и други методи, които да използваме. Ето списъка с методи:

Когато пуснем теста, виждаме, че той преминава.

Как да тестваме услугите?

Сега отиваме да тестваме нашата UserService. Доста е лесно да се тества.

Подиграваме хранилището и инжектираме нашите подигравки в UserService. Сега, когато пуснем теста, ще видим, че той преминава.

Сега нека добавим бизнес правило към UserService: да речем, че потребителят трябва да има имейл адрес.

Променяме метода си за запис в UserService, както е показано по-долу:

public User save(CreateUserRequest request) { requireNonNull(request.getEmail()); User user = new User(); user.setName(request.getName()); user.setEmail(request.getEmail()); user.setAge(request.getAge()); userRepository.save(user); return user;}

Когато стартираме теста отново, ще видим неуспешен тест.

Преди да го поправим, нека напишем тест, който удовлетворява този бизнес.

Написахме нов тест, който уточнява, че ако изпратим нулев имейл, той ще хвърли NullPointerException.

Нека поправим неуспешния тест, като добавим имейл към нашата заявка:

createUserRequest.setEmail("testemail");

Изпълнете и двата теста:

Как да тестваме хранилища?

Сега стигнахме до тестване на хранилища. Използваме база данни в памет h2 сTestEntityManager.

Нашето хранилище е дефинирано по-долу:

@Repositorypublic interface UserRepository extends JpaRepository, JpaSpecificationExecutor { Optional findById(UUID id);}

Първо конфигурирайте h2db. Създайте името на файла application.yaml в test -> path path:

spring: application: name: Spring Boot Rest API datasource: type: com.zaxxer.hikari.HikariDataSource url: "jdbc:h2:mem:test-api;INIT=CREATE SCHEMA IF NOT EXISTS dbo\\;CREATE SCHEMA IF NOT EXISTS definitions;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false;MODE=MSSQLServer" name: password: username: initialization-mode: never hikari: schema: dbo jpa: database: H2 database-platform: org.hibernate.dialect.H2Dialect show-sql: true hibernate: ddl-auto: create-drop test: database: replace: none

И нека първо напишем основен тест за нашето хранилище: запишете потребител и го извлечете:

@RunWith(SpringRunner.class)@DataJpaTestpublic class UserRepositoryTest { @Autowired TestEntityManager entityManager; @Autowired UserRepository sut; @Test public void it_should_save_user() { User user = new User(); user.setName("test user"); user = entityManager.persistAndFlush(user); assertThat(sut.findById(user.getId()).get()).isEqualTo(user); }}

Когато го стартираме, ще видим куп изводи на конзолата, както и нашите тестови изходи:

Сега нека добавим друг метод към нашето хранилище за търсене на потребител по имейл:

Optional findByEmail(String email);

И напишете още един тест:

@Testpublic void it_should_find_user_byEmail() { User user = new User(); user.setEmail("[email protected]"); user = entityManager.persistAndFlush(user); assertThat(sut.findByEmail(user.getEmail()).get()).isEqualTo(user);}

Когато погледнем конзолата след стартиране на теста, ще видим SQL, генериран от хибернация:

SELECT user0_.id AS id1_1_,user0_.age AS age2_1_,user0_.email AS email3_1_,user0_.name AS name4_1_FROM user user0_WHERE user0_.email=?

Дотук добре. Покрихме основите на модулното тестване с пружинно зареждане.

Now you don’t have any excuses to not write unit tests! I hope it is clear to you to how to write unit tests for different kinds of purposes.