Ujian di Spring Boot

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan melihat ujian menulis menggunakan sokongan kerangka di Spring Boot. Kami akan merangkumi ujian unit yang dapat dijalankan secara terpisah dan juga ujian integrasi yang akan menguatkan konteks Spring sebelum menjalankan ujian.

Sekiranya anda baru menggunakan Spring Boot, lihat pengenalan kami ke Spring Boot.

2. Penyediaan Projek

Aplikasi yang akan kita gunakan dalam artikel ini adalah API yang menyediakan beberapa operasi dasar pada Sumber Karyawan . Ini adalah seni bina berjenjang khas - panggilan API diproses dari Controller to Service ke lapisan Persistence .

3. Pergantungan Maven

Mari tambahkan kebergantungan ujian kami terlebih dahulu:

 org.springframework.boot spring-boot-starter-test test 2.2.6.RELEASE   com.h2database h2 test 

Ujian spring-boot-starter adalah kebergantungan utama yang mengandungi sebahagian besar elemen yang diperlukan untuk ujian kami.

DB H2 adalah pangkalan data dalam memori kami. Ini menghilangkan keperluan untuk mengkonfigurasi dan memulakan pangkalan data sebenar untuk tujuan ujian.

4. Ujian Integrasi Dengan @DataJpaTest

Kami akan bekerjasama dengan entiti bernama Karyawan, yang mempunyai id dan nama sebagai sifatnya:

@Entity @Table(name = "person") public class Employee { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Size(min = 3, max = 20) private String name; // standard getters and setters, constructors }

Dan inilah repositori kami menggunakan Spring Data JPA:

@Repository public interface EmployeeRepository extends JpaRepository { public Employee findByName(String name); }

Itu sahaja untuk kod lapisan kegigihan. Sekarang mari kita menuju ke kelas ujian kita.

Pertama, mari buat kerangka kelas ujian kami:

@RunWith(SpringRunner.class) @DataJpaTest public class EmployeeRepositoryIntegrationTest { @Autowired private TestEntityManager entityManager; @Autowired private EmployeeRepository employeeRepository; // write test cases here }

@RunWith (SpringRunner.class) menyediakan jambatan antara ciri ujian Spring Boot dan JUnit. Setiap kali kami menggunakan sebarang ciri pengujian Spring Boot dalam ujian JUnit kami, anotasi ini akan diperlukan.

@DataJpaTest menyediakan beberapa persediaan standard yang diperlukan untuk menguji lapisan ketekunan:

  • mengkonfigurasi H2, pangkalan data dalam memori
  • menetapkan Hibernate, Spring Data, dan DataSource
  • melakukan @EntityScan
  • menghidupkan pembalakan SQL

Untuk menjalankan operasi DB, kami memerlukan beberapa rekod yang sudah ada di pangkalan data kami. Untuk menyiapkan data ini, kita dapat menggunakan TestEntityManager.

Spring Boot TestEntityManager adalah alternatif kepada JPA EntityManager standard yang menyediakan kaedah yang biasa digunakan semasa menulis ujian.

EmployeeRepository adalah komponen yang akan kita uji.

Sekarang mari tulis kes ujian pertama kami:

@Test public void whenFindByName_thenReturnEmployee() { // given Employee alex = new Employee("alex"); entityManager.persist(alex); entityManager.flush(); // when Employee found = employeeRepository.findByName(alex.getName()); // then assertThat(found.getName()) .isEqualTo(alex.getName()); }

Dalam ujian di atas, kami menggunakan TestEntityManager untuk memasukkan Karyawan di DB dan membacanya melalui API temukan dengan nama.

Bahagian assertThat (…) berasal dari perpustakaan Assertj, yang dibundel dengan Spring Boot.

5. Mengejek Dengan @MockBean

Kod lapisan Perkhidmatan kami bergantung pada Repositori kami .

Namun, untuk menguji lapisan Perkhidmatan , kami tidak perlu tahu atau peduli bagaimana lapisan ketekunan dilaksanakan:

@Service public class EmployeeServiceImpl implements EmployeeService { @Autowired private EmployeeRepository employeeRepository; @Override public Employee getEmployeeByName(String name) { return employeeRepository.findByName(name); } }

Sebaik-baiknya, kita seharusnya dapat menulis dan menguji kod lapisan Perkhidmatan kita tanpa pendawaian di lapisan ketekunan penuh kita.

Untuk mencapainya, kita boleh menggunakan sokongan mengejek yang disediakan oleh Spring Boot Test.

Mari lihat kerangka kelas ujian terlebih dahulu:

@RunWith(SpringRunner.class) public class EmployeeServiceImplIntegrationTest { @TestConfiguration static class EmployeeServiceImplTestContextConfiguration { @Bean public EmployeeService employeeService() { return new EmployeeServiceImpl(); } } @Autowired private EmployeeService employeeService; @MockBean private EmployeeRepository employeeRepository; // write test cases here }

Untuk memeriksa kelas Perkhidmatan , kita perlu membuat contoh kelas Perkhidmatan yang dibuat dan tersedia sebagai @Bean sehingga kita dapat @Autowire di kelas ujian kita. Kami dapat mencapai konfigurasi ini dengan menggunakan anotasi @TestConfiguration .

Semasa pengimbasan komponen, kami mungkin mendapati bahawa komponen atau konfigurasi yang dibuat hanya untuk ujian tertentu tidak dapat dijumpai di mana-mana sahaja. Untuk membantu mencegahnya, Spring Boot menyediakan anotasi @TestConfiguration yang dapat kita tambahkan pada kelas di src / test / java untuk menunjukkan bahawa mereka tidak boleh diambil dengan mengimbas.

Satu lagi perkara menarik di sini ialah penggunaan @MockBean . Ini membuat Mock untuk EmployeeRepository , yang dapat digunakan untuk memotong panggilan ke EmployeeRepository yang sebenarnya :

@Before public void setUp() { Employee alex = new Employee("alex"); Mockito.when(employeeRepository.findByName(alex.getName())) .thenReturn(alex); }

Oleh kerana persediaan selesai, kes ujian akan lebih mudah:

@Test public void whenValidName_thenEmployeeShouldBeFound() { String name = "alex"; Employee found = employeeService.getEmployeeByName(name); assertThat(found.getName()) .isEqualTo(name); }

6. Ujian Unit Dengan @WebMvcTest

Pengawal kami bergantung pada lapisan Perkhidmatan ; mari kita sertakan satu kaedah untuk kesederhanaan:

@RestController @RequestMapping("/api") public class EmployeeRestController { @Autowired private EmployeeService employeeService; @GetMapping("/employees") public List getAllEmployees() { return employeeService.getAllEmployees(); } }

Oleh kerana kami hanya tertumpu pada kod Pengawal , adalah wajar untuk mengejek kod lapisan Perkhidmatan untuk ujian unit kami:

@RunWith(SpringRunner.class) @WebMvcTest(EmployeeRestController.class) public class EmployeeRestControllerIntegrationTest { @Autowired private MockMvc mvc; @MockBean private EmployeeService service; // write test cases here }

Untuk menguji Pengawal , kita boleh menggunakan @WebMvcTest . Ia akan mengkonfigurasi secara automatik infrastruktur Spring MVC untuk ujian unit kami.

Dalam kebanyakan kes, @ WebMvcTest akan terhad kepada bootstrap satu pengawal. Kami juga dapat menggunakannya bersama dengan @MockBean untuk memberikan implementasi tiruan untuk setiap dependensi yang diperlukan.

@WebMvcTest juga mengkonfigurasi secara automatik MockMvc , yang menawarkan cara yang kuat untuk mudah menguji pengawal MVC tanpa memulakan pelayan HTTP penuh.

Setelah itu, mari tulis kes ujian kami:

@Test public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception { Employee alex = new Employee("alex"); List allEmployees = Arrays.asList(alex); given(service.getAllEmployees()).willReturn(allEmployees); mvc.perform(get("/api/employees") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasSize(1))) .andExpect(jsonPath("$[0].name", is(alex.getName()))); }

The get (...) kaedah panggilan boleh digantikan dengan kaedah lain yang sepadan dengan HTTP kata kerja seperti put () , pos () , dan lain-lain Sila ambil perhatian bahawa kami juga menetapkan jenis kandungan dalam permintaan itu.

MockMvc fleksibel, dan kami dapat membuat permintaan menggunakannya.

7. Ujian Integrasi Dengan @SpringBootTest

Seperti namanya, ujian integrasi menumpukan pada penyatuan lapisan aplikasi yang berbeza. Itu juga bermaksud tidak mengejek.

Sebaik-baiknya, kita harus memisahkan ujian integrasi daripada ujian unit dan tidak boleh dijalankan bersamaan dengan ujian unit. Kita boleh melakukan ini dengan menggunakan profil yang berbeza untuk hanya menjalankan ujian integrasi. Beberapa sebab untuk melakukan ini mungkin kerana ujian integrasi memakan masa dan mungkin memerlukan pangkalan data yang sebenarnya untuk dilaksanakan.

Namun dalam artikel ini, kami tidak akan memfokuskannya, dan sebaliknya kami akan menggunakan penyimpanan ketekunan H2 dalam memori.

Ujian integrasi perlu memulakan wadah untuk melaksanakan kes ujian. Oleh itu, beberapa persediaan tambahan diperlukan untuk ini - semua ini mudah dilakukan di Spring Boot:

@RunWith(SpringRunner.class) @SpringBootTest( SpringBootTest.WebEnvironment.MOCK, classes = Application.class) @AutoConfigureMockMvc @TestPropertySource( locations = "classpath:application-integrationtest.properties") public class EmployeeRestControllerIntegrationTest { @Autowired private MockMvc mvc; @Autowired private EmployeeRepository repository; // write test cases here }

The @SpringBootTest anotasi berguna apabila kita perlu bootstrap keseluruhan bekas. Anotasi berfungsi dengan membuat ApplicationContext yang akan digunakan dalam ujian kami.

Kita boleh menggunakan webEnvironment sifat @SpringBootTest untuk mengkonfigurasi persekitaran runtime kami; kami menggunakan WebEnvironment.MOCK di sini supaya kontena akan beroperasi dalam persekitaran servlet tiruan.

Seterusnya, anotasi @TestPropertySource membantu mengkonfigurasi lokasi fail harta tanah khusus untuk ujian kami. Perhatikan bahawa fail harta tanah yang dimuat dengan @TestPropertySource akan mengatasi fail application.properties yang ada .

The application-integrationtest.properties contains the details to configure the persistence storage:

spring.datasource.url = jdbc:h2:mem:test spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect

If we want to run our integration tests against MySQL, we can change the above values in the properties file.

The test cases for the integration tests might look similar to the Controller layer unit tests:

@Test public void givenEmployees_whenGetEmployees_thenStatus200() throws Exception { createTestEmployee("bob"); mvc.perform(get("/api/employees") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content() .contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$[0].name", is("bob"))); }

The difference from the Controller layer unit tests is that here nothing is mocked and end-to-end scenarios will be executed.

8. Auto-Configured Tests

One of the amazing features of Spring Boot's auto-configured annotations is that it helps to load parts of the complete application and test-specific layers of the codebase.

In addition to the above-mentioned annotations, here's a list of a few widely used annotations:

  • @WebFluxTest: We can use the @WebFluxTest annotation to test Spring WebFlux controllers. It's often used along with @MockBean to provide mock implementations for required dependencies.
  • @JdbcTest: We can use the @JdbcTest annotation to test JPA applications, but it's for tests that only require a DataSource. The annotation configures an in-memory embedded database and a JdbcTemplate.
  • @JooqTest: To test jOOQ-related tests, we can use @JooqTest annotation, which configures a DSLContext.
  • @DataMongoTest: To test MongoDB applications, @DataMongoTest is a useful annotation. By default, it configures an in-memory embedded MongoDB if the driver is available through dependencies, configures a MongoTemplate, scans for @Document classes, and configures Spring Data MongoDB repositories.
  • @DataRedisTestmakes it easier to test Redis applications. It scans for @RedisHash classes and configures Spring Data Redis repositories by default.
  • @DataLdapTest configures an in-memory embedded LDAP (if available), configures a LdapTemplate, scans for @Entry classes, and configures Spring Data LDAP repositories by default.
  • @RestClientTest: We generally use the @RestClientTest annotation to test REST clients. It auto-configures different dependencies such as Jackson, GSON, and Jsonb support; configures a RestTemplateBuilder; and adds support for MockRestServiceServer by default.

9. Conclusion

In this article, we took a deep dive into the testing support in Spring Boot and showed how to write unit tests efficiently.

Kod sumber lengkap artikel ini boleh didapati di GitHub. Kod sumber mengandungi lebih banyak contoh dan pelbagai kes ujian.

Dan jika anda ingin terus belajar mengenai ujian, kami mempunyai artikel berasingan yang berkaitan dengan ujian integrasi dan ujian unit di JUnit 5.