Menguji di Spring Boot

1. Ikhtisar

Dalam tutorial ini, kita akan melihat tes menulis menggunakan dukungan kerangka kerja di Spring Boot. Kami akan membahas pengujian unit yang dapat berjalan secara terpisah serta pengujian integrasi yang akan melakukan bootstrap dalam konteks Spring sebelum menjalankan pengujian.

Jika Anda baru mengenal Spring Boot, lihat intro kami tentang Spring Boot.

2. Pengaturan Proyek

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

3. Ketergantungan Maven

Pertama-tama, tambahkan dependensi pengujian kami:

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

The semi-boot-starter-test adalah ketergantungan utama yang berisi mayoritas elemen yang dibutuhkan untuk tes kami.

H2 DB adalah database dalam memori kami. Ini menghilangkan kebutuhan untuk mengkonfigurasi dan memulai database sebenarnya untuk tujuan pengujian.

4. Pengujian Integrasi Dengan @DataJpaTest

Kami akan bekerja dengan entitas bernama Karyawan, yang memiliki id dan nama sebagai propertinya:

@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 untuk kode lapisan ketekunan. Sekarang mari kita mulai menulis kelas pengujian kita.

Pertama, mari buat kerangka kelas pengujian kita:

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

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

@DataJpaTest menyediakan beberapa penyiapan standar yang diperlukan untuk menguji lapisan persistensi:

  • mengkonfigurasi H2, database dalam memori
  • mengatur Hibernate, Spring Data, dan DataSource
  • melakukan @EntityScan
  • mengaktifkan logging SQL

Untuk menjalankan operasi DB, kita membutuhkan beberapa record di database kita. Untuk menyiapkan data ini, kita dapat menggunakan TestEntityManager.

Spring Boot TestEntityManager adalah alternatif dari JPA EntityManager standar yang menyediakan metode yang biasa digunakan saat menulis pengujian.

EmployeeRepository adalah komponen yang akan kita uji.

Sekarang mari kita tulis kasus uji pertama kita:

@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 pengujian di atas, kami menggunakan TestEntityManager untuk menyisipkan Karyawan di DB dan membacanya melalui API find by name.

Bagian assertThat (…) berasal dari pustaka Assertj, yang dibundel dengan Spring Boot.

5. Mengolok- olok Dengan @MockBean

Kode lapisan Layanan kami bergantung pada Repositori kami .

Namun, untuk menguji lapisan Layanan , kita tidak perlu mengetahui atau peduli tentang bagaimana lapisan persistensi diimplementasikan:

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

Idealnya, kami harus dapat menulis dan menguji kode lapisan Layanan kami tanpa kabel di lapisan persistensi penuh kami.

Untuk mencapai ini, kita dapat menggunakan dukungan mengejek yang disediakan oleh Spring Boot Test.

Mari kita lihat kerangka kelas pengujian 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 Service , kita perlu membuat turunan dari kelas Service yang dibuat dan tersedia sebagai @Bean sehingga kita bisa @Autowire di kelas pengujian kita. Kita dapat mencapai konfigurasi ini menggunakan anotasi @TestConfiguration .

Selama pemindaian komponen, kami mungkin menemukan bahwa komponen atau konfigurasi yang dibuat hanya untuk pengujian tertentu secara tidak sengaja terambil di mana-mana. Untuk membantu mencegah hal ini, Spring Boot menyediakan anotasi @TestConfiguration yang dapat kita tambahkan pada kelas di src / test / java untuk menunjukkan bahwa mereka tidak boleh diambil oleh pemindaian.

Hal menarik lainnya di sini adalah penggunaan @MockBean . Ini membuat Mock untuk EmployeeRepository , yang bisa digunakan untuk melewati panggilan ke EmployeeRepository yang sebenarnya :

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

Sejak penyiapan selesai, kasus uji akan lebih sederhana:

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

6. Pengujian Unit Dengan @WebMvcTest

Kontroler kami bergantung pada lapisan Layanan ; mari kita hanya menyertakan satu metode untuk kesederhanaan:

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

Karena kami hanya fokus pada kode Pengontrol , wajar untuk meniru kode lapisan Layanan untuk pengujian 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 Pengontrol , kita dapat menggunakan @WebMvcTest . Ini akan secara otomatis mengkonfigurasi infrastruktur MVC Spring untuk pengujian unit kami.

Dalam kebanyakan kasus, @ WebMvcTest akan dibatasi untuk bootstrap pengontrol tunggal. Kami juga dapat menggunakannya bersama dengan @MockBean untuk menyediakan implementasi tiruan untuk setiap dependensi yang diperlukan.

@WebMvcTest juga mengonfigurasi MockMvc secara otomatis , yang menawarkan cara ampuh untuk menguji pengontrol MVC dengan mudah tanpa memulai server HTTP penuh.

Karena itu, mari kita tulis kasus uji kita:

@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 (...) metode panggilan bisa digantikan oleh metode lain yang sesuai dengan HTTP verba seperti put () , posting () , dll Silahkan catatan bahwa kita juga menetapkan jenis konten dalam permintaan.

MockMvc fleksibel, dan kami dapat membuat permintaan apa pun menggunakannya.

7. Pengujian Integrasi Dengan @SpringBootTest

Seperti namanya, tes integrasi berfokus pada integrasi berbagai lapisan aplikasi. Itu juga berarti tidak ada ejekan yang terlibat.

Idealnya, kita harus memisahkan pengujian integrasi dari pengujian unit dan tidak boleh berjalan bersama dengan pengujian unit. Kami dapat melakukan ini dengan menggunakan profil yang berbeda untuk hanya menjalankan tes integrasi. Beberapa alasan untuk melakukan ini mungkin karena pengujian integrasi memakan waktu dan mungkin memerlukan database aktual untuk dijalankan.

Namun dalam artikel ini, kami tidak akan fokus pada hal itu, dan sebagai gantinya kami akan menggunakan penyimpanan persistensi H2 dalam memori.

Pengujian integrasi perlu memulai container untuk menjalankan kasus pengujian. Karenanya, beberapa pengaturan tambahan diperlukan untuk ini - semua ini mudah 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 penjelasan berguna ketika kita perlu untuk bootstrap seluruh kontainer. Anotasi bekerja dengan membuat ApplicationContext yang akan digunakan dalam pengujian kami.

Kita dapat menggunakan webEnvironment atribut @SpringBootTest untuk mengkonfigurasi lingkungan runtime kami; kami menggunakan WebEnvironment.MOCK di sini sehingga container akan beroperasi di lingkungan servlet tiruan.

Selanjutnya, anotasi @TestPropertySource membantu mengonfigurasi lokasi file properti yang spesifik untuk pengujian kami. Perhatikan bahwa file properti yang dimuat dengan @TestPropertySource akan mengganti file 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.

Kode sumber lengkap artikel ini dapat ditemukan di GitHub. Kode sumber berisi lebih banyak contoh dan berbagai kasus uji.

Dan jika Anda ingin terus belajar tentang pengujian, kami memiliki artikel terpisah terkait pengujian integrasi dan pengujian unit di JUnit 5.