Spring Bootでのテスト
1. 概要
この記事では、writing tests using the framework support in Spring Bootについて説明します。 単独で実行できる単体テストと、テストを実行する前にSpringコンテキストをブートストラップする統合テストについて説明します。
Spring Bootを初めて使用する場合は、intro to Spring Bootを確認してください。
参考文献:
MockitoモックをSpring Beanに注入する
この記事では、依存関係注入を使用して、Mockitoモックを単体テスト用のSpring Beanに挿入する方法を示します。
2. プロジェクトのセットアップ
この記事で使用するアプリケーションは、Employeeリソースに対するいくつかの基本的な操作を提供するAPIです。 これは典型的な階層型アーキテクチャです。API呼び出しはControllerからService、そしてPersistenceレイヤーまで処理されます。
3. Mavenの依存関係
まず、テストの依存関係を追加しましょう。
org.springframework.boot
spring-boot-starter-test
test
2.1.6.RELEASE
com.h2database
h2
test
1.4.194
spring-boot-starter-testは、テストに必要な要素の大部分を含む主要な依存関係です。
H2 DBはインメモリデータベースです。 テスト目的で実際のデータベースを構成して起動する必要がなくなります。
4. @DataJpaTestとの統合テスト
プロパティとしてidとnameを持つ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テストでSpring Bootテスト機能を使用するときは常に、このアノテーションが必要になります。
@DataJpaTestは、永続層のテストに必要ないくつかの標準セットアップを提供します。
-
H2、メモリ内データベースの構成
-
Hibernate、Spring Data、およびDataSourceの設定
-
@EntityScanを実行する
-
SQLロギングをオンにする
何らかのDB操作を実行するには、データベースにすでに設定されているいくつかのレコードが必要です。 このデータを設定するには、TestEntityManager.The TestEntityManager provided by Spring Boot is an alternative to the standard JPA EntityManager that provides methods commonly used when writing tests.を使用できます。
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を使用してEmployeeをDBに挿入し、名前による検索APIを介して読み取ります。
assertThat(…)の部分は、Spring BootにバンドルされているAssertj libraryに由来します。
5. @MockBeanでモックする
ServiceレイヤーコードはRepositoryに依存しています。 ただし、Serviceレイヤーをテストするために、永続レイヤーがどのように実装されているかを知ったり気にしたりする必要はありません。
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Override
public Employee getEmployeeByName(String name) {
return employeeRepository.findByName(name);
}
}
理想的には、完全な永続化レイヤーに配線することなく、サービスレイヤーコードを記述およびテストできる必要があります。
これを実現するには、we can use the mocking support provided by Spring Boot Testを使用します。
最初にテストクラスのスケルトンを見てみましょう。
@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
}
Serviceクラスを確認するには、Serviceクラスのインスタンスを作成し、@Beanとして使用できるようにして、テストクラスで@Autowireできるようにする必要があります。 この構成は、@TestConfigurationアノテーションを使用して実現されます。
コンポーネントのスキャン中に、特定のテスト用に作成されたコンポーネントまたは構成が偶然どこにでも見つかる可能性があります。 それを防ぐために、Spring Boot provides @TestConfiguration annotation that can be used on classes in src/test/java to indicate that they should not be picked up by scanning.
ここでのもう1つの興味深い点は、@MockBeanの使用です。 実際のEmployeeRepositoryへの呼び出しをバイパスするために使用できるEmployeeRepositoryの場合はcreates a Mockです。
@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を使用したユニットテスト
ControllerはServiceレイヤーに依存します。簡単にするために、1つの方法だけを含めましょう。
@RestController
@RequestMapping("/api")
public class EmployeeRestController {
@Autowired
private EmployeeService employeeService;
@GetMapping("/employees")
public List getAllEmployees() {
return employeeService.getAllEmployees();
}
}
Controllerコードのみに焦点を当てているため、単体テスト用にServiceレイヤーコードをモックするのは自然なことです。
@RunWith(SpringRunner.class)
@WebMvcTest(EmployeeRestController.class)
public class EmployeeRestControllerIntegrationTest {
@Autowired
private MockMvc mvc;
@MockBean
private EmployeeService service;
// write test cases here
}
Controllersをテストするには、@WebMvcTestを使用できます。 単体テスト用にSpring MVCインフラストラクチャを自動構成します。
ほとんどの場合、@WebMvcTestは、単一のコントローラーのブートストラップに制限されます。 @MockBeanと一緒に使用して、必要な依存関係の模擬実装を提供します。
@WebMvcTestはMockMvcも自動構成します。これは、完全なHTTPサーバーを起動せずにMVCコントローラーを簡単にテストする強力な方法を提供します。
そうは言っても、テストケースを書いてみましょう。
@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())));
}
get(…)メソッド呼び出しは、put()、post()などのHTTP動詞に対応する他のメソッドに置き換えることができます。 リクエストでコンテンツタイプも設定していることに注意してください。
MockMvcは柔軟性があり、それを使用して任意のリクエストを作成できます。
7. @SpringBootTestとの統合テスト
名前が示すように、統合テストはアプリケーションのさまざまなレイヤーの統合に焦点を当てています。 それはまた、モッキングが含まれていないことを意味します。
Ideally, we should keep the integration tests separated from the unit tests and should not run along with the unit tests.別のプロファイルを使用して統合テストのみを実行することで、これを実行できます。 これを行う理由は、統合テストに時間がかかり、実行するには実際のデータベースが必要になる可能性があるためです。
ただし、この記事ではこれに焦点を当てず、代わりにメモリ内のH2永続ストレージを利用します。
統合テストでは、テストケースを実行するためにコンテナを起動する必要があります。 したがって、これにはいくつかの追加設定が必要です。これはすべて、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
}
@SpringBootTestアノテーションは、コンテナー全体をブートストラップする必要がある場合に使用できます。 アノテーションは、テストで使用されるApplicationContextを作成することで機能します。
@SpringBootTestのwebEnvironment属性を使用して、ランタイム環境を構成できます。ここではWebEnvironment.MOCKを使用しているため、コンテナは模擬サーブレット環境で動作します。
@TestPropertySourceアノテーションを使用して、テストに固有のプロパティファイルの場所を構成できます。 @TestPropertySourceがロードされたプロパティファイルは、既存のapplication.propertiesファイルを上書きすることに注意してください。
application-integrationtest.propertiesには、永続ストレージを構成するための詳細が含まれています。
spring.datasource.url = jdbc:h2:mem:test
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
MySQLに対して統合テストを実行する場合は、プロパティファイルで上記の値を変更できます。
統合テストのテストケースは、Controllerレイヤーユニットテストのように見える場合があります。
@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")));
}
Controllerレイヤーユニットテストとの違いは、ここでは何もモックされておらず、エンドツーエンドのシナリオが実行されることです。
8. 自動構成テスト
Spring Bootの自動構成されたアノテーションの驚くべき機能の1つは、アプリケーション全体の一部をロードし、コードベースの特定のレイヤーをテストするのに役立つことです。
上記の注釈に加えて、広く使用されている注釈のリストを次に示します。
-
@WebFluxTest– weは、@WebFluxTestアノテーションを使用してSpringWebfluxコントローラーをテストできます。 必要な依存関係の模擬実装を提供するために、@MockBeanと一緒に使用されることがよくあります。
-
@JdbcTest – weは@JdbcTestアノテーションを使用してJPAアプリケーションをテストできますが、DataSource.のみを必要とするテスト用です。アノテーションはインメモリ組み込みデータベースとJdbcTemplate.を構成します。
-
@JooqTest – jOOQ関連のテストをテストするには、DSLContextを構成する@JooqTestアノテーションを使用できます。
-
@DataMongoTest – MongoDBアプリケーションをテストするには@DataMongoTestが便利なアノテーションです。 デフォルトでは、依存関係を通じてドライバーが利用可能な場合は、メモリー内の組み込みMongoDBを構成し、@DocumentクラスのMongoTemplate,スキャンを構成し、Spring DataMongoDBリポジトリーを構成します。
-
@DataRedisTest – makes it easier to testRedisアプリケーション。 @RedisHashクラスをスキャンし、デフォルトでSpring DataRedisリポジトリーを構成します。
-
@DataLdapTest –は、メモリ内に埋め込まれたLDAP(使用可能な場合)を構成し、LdapTemplateを構成し、@Entryクラスをスキャンし、デフォルトでSpring DataLDAPリポジトリーを構成します
-
@RestClientTest – weは通常、@RestClientTestアノテーションを使用してRESTクライアントをテストします。 Jackson、GSON、Jsonbのサポートなどのさまざまな依存関係を自動構成し、RestTemplateBuilderを構成し、デフォルトでMockRestServiceServerのサポートを追加します。
9. 結論
このチュートリアルでは、Spring Bootのテストサポートを深く掘り下げ、ユニットテストを効率的に作成する方法を示しました。
この記事の完全なソースコードはfound over on GitHubです。 ソースコードには、さらに多くの例とさまざまなテストケースが含まれています。
また、テストについて学び続けたい場合は、integration testsとunit tests in JUnit 5に関連する個別の記事があります。