SpringBootでのテスト

1。概要

このチュートリアルでは、SpringBootのフレームワークサポートを使用てテストを作成する方法について説明します。単独で実行できる単体テストと、テストを実行する前にSpringコンテキストをブートストラップする統合テストについて説明します。

Spring Bootを初めて使用する場合は、SpringBootの概要を確認してください。

2.プロジェクトのセットアップ

この記事で使用するアプリケーションは、従業員リソースに対するいくつかの基本的な操作を提供するAPIです。これは典型的な階層型アーキテクチャです。API呼び出しは、コントローラーからサービス、そして永続性レイヤーに処理されます

3.Mavenの依存関係

まず、テストの依存関係を追加しましょう。

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

春・ブート・スターター・テストでは、我々のテストのために必要な要素の大半が含まれている主要な依存関係です。

H2 DBは、インメモリデータベースです。これにより、テスト目的で実際のデータベースを構成して開始する必要がなくなります。

4. @ DataJpaTestとの統合テスト

プロパティとしてID名前を持つEmployeeという名前のエンティティを操作します。

@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 }

そして、Spring DataJPAを使用したリポジトリは次のとおりです。

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

永続層コードは以上です。それでは、テストクラスの作成に取り掛かりましょう。

まず、テストクラスのスケルトンを作成しましょう。

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

@RunWith(SpringRunner.class)は、SpringBootテスト機能とJUnitの間のブリッジを提供します。JUnitテストでSpringBootテスト機能を使用する場合は常に、このアノテーションが必要になります。

@DataJpaTestは、永続層のテストに必要ないくつかの標準セットアップを提供します。

  • インメモリデータベースであるH2の構成
  • Hibernate、Spring Data、およびDataSourceの設定
  • @EntityScanの実行
  • SQLログをオンにする

DB操作を実行するには、データベースにすでにいくつかのレコードが必要です。このデータを設定するには、TestEntityManagerを使用できます

春ブーツTestEntityManagerは、標準のJPAの代替であるEntityManagerのテストを書くときに一般的に使用されるメソッドを提供します。

EmployeeRepositoryは、テストするコンポーネントです。

それでは、最初のテストケースを書いてみましょう。

@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()); }

上記のテストでは、TestEntityManagerを使用してDBに従業員を挿入し、名前による検索APIを介してそれを読み取ります。

assertThat(...)の部分は、春ブーツがバンドルされていAssertjライブラリー、から来ています。

5. @ MockBeanを使用したモック

当社のサービス層のコードは、私たちに依存しているリポジトリ

ただし、サービスレイヤーをテストするために、永続レイヤーがどのように実装されているかを知る必要はありません。

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

Ideally, we should be able to write and test our Service layer code without wiring in our full persistence layer.

To achieve this, we can use the mocking support provided by Spring Boot Test.

Let's have a look at the test class skeleton first:

@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 }

To check the Service class, we need to have an instance of the Service class created and available as a @Bean so that we can @Autowire it in our test class. We can achieve this configuration using the @TestConfiguration annotation.

コンポーネントのスキャン中に、特定のテスト専用に作成されたコンポーネントまたは構成が誤ってどこにでも表示される場合があります。これを防ぐために、Spring Bootには@TestConfigurationアノテーションが用意されており、これsrc / test / javaのクラスに追加して、スキャンで取得しないように指示できます。

ここでもう1つ興味深いのは、@ MockBeanの使用です。EmployeeRepositoryのモックを作成します。これを使用して、実際のEmployeeRepositoryへの呼び出しをバイパスできます。

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

セットアップが完了しているため、テストケースはより単純になります。

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

6. @ WebMvcTestを使用したユニットテスト

Our Controller depends on the Service layer; let's only include a single method for simplicity:

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

Since we're only focused on the Controller code, it's natural to mock the Service layer code for our unit tests:

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

To test the Controllers, we can use @WebMvcTest. It will auto-configure the Spring MVC infrastructure for our unit tests.

In most cases, @WebMvcTest will be limited to bootstrap a single controller. We can also use it along with @MockBean to provide mock implementations for any required dependencies.

@WebMvcTest also auto-configures MockMvc, which offers a powerful way of easy testing MVC controllers without starting a full HTTP server.

Having said that, let's write our test case:

@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(…) method call can be replaced by other methods corresponding to HTTP verbs like put(), post(), etc. Please note that we are also setting the content type in the request.

MockMvc is flexible, and we can create any request using it.

7. Integration Testing With @SpringBootTest

As the name suggests, integration tests focus on integrating different layers of the application. That also means no mocking is involved.

Ideally, we should keep the integration tests separated from the unit tests and should not run along with the unit tests. We can do this by using a different profile to only run the integration tests. A couple of reasons for doing this could be that the integration tests are time-consuming and might need an actual database to execute.

However in this article, we won't focus on that, and we'll instead make use of the in-memory H2 persistence storage.

The integration tests need to start up a container to execute the test cases. Hence, some additional setup is required for this — all of this is easy in 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 annotation is useful when we need to bootstrap the entire container. The annotation works by creating the ApplicationContext that will be utilized in our tests.

We can use the webEnvironment attribute of @SpringBootTest to configure our runtime environment; we're using WebEnvironment.MOCK here so that the container will operate in a mock servlet environment.

Next, the @TestPropertySource annotation helps configure the locations of properties files specific to our tests. Note that the property file loaded with @TestPropertySource will override the existing application.properties file.

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.

この記事の完全なソースコードはGitHubにあります。ソースコードには、さらに多くの例とさまざまなテストケースが含まれています。

また、テストについて学び続けたい場合は、JUnit5の統合テストと単体テストに関連する個別の記事があります。