単体テスト作成
単体テストの書き方およびカバレッジの確認方法を説明します。
cf. https://spring.pleiades.io/spring-boot/reference/testing/spring-boot-applications.html
事前準備
下記設定を追記します:
- test/resources/application-test.properties
# エンティティクラスからスキーマを自動生成しない
spring.jpa.hibernate.ddl-auto=none
# h2db接続設定
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
- pom.xml
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
- XxxApplicationTests.java
// test配下の自動生成済みテストクラスに下記を追記
@ActiveProfiles("test")
作成手順
前提
下記テーブルに対してデータを取得 / 登録する API を想定しています:
| カラム名 | 属性 |
|---|---|
| name | VARCHAR(8) PRIMARY KEY |
| password | VARCHAR(32) |
| age | INT |
controller テスト作成
package nob.example.easyapp.controller;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import nob.example.easyapp.controller.model.LoginRequest;
import nob.example.easyapp.controller.model.LoginResponse;
import nob.example.easyapp.controller.model.MeRequest;
import nob.example.easyapp.controller.model.MeResponse;
import nob.example.easyapp.service.AuthService;
import nob.example.easyapp.service.model.LoginInModel;
import nob.example.easyapp.service.model.LoginOutModel;
import nob.example.easyapp.service.model.MeInModel;
import nob.example.easyapp.service.model.MeOutModel;
/**
* AuthControllerImplのテストクラスです。
*
* @author nob
*/
@WebMvcTest
public class AuthControllerImplTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockitoBean
private AuthService authService;
/**
* loginのテスト 正常系
*/
@Test
void test_login_success() {
// リクエストの作成
LoginRequest request = new LoginRequest("nob", "passwd");
// serviceのモック化
Mockito.when(authService.login(new LoginInModel(request.getName(), request.getPassword())))
.thenReturn(new LoginOutModel(true));
try {
// API呼び出し
MvcResult result = mockMvc.perform(post("/api/v1/login")
.content(objectMapper.writeValueAsString(request))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn();
// 結果のassert
assertThat(result.getResponse().getContentAsString())
.isEqualTo(objectMapper.writeValueAsString(new LoginResponse(true)));
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
/**
* meのテスト 正常系
*/
@Test
void test_me_success() {
// リクエストの作成
MeRequest request = new MeRequest("nob");
// serviceのモック化
Mockito.when(authService.me(new MeInModel(request.getName()))).thenReturn(new MeOutModel("nob", 13));
try {
// API呼び出し
MvcResult result = mockMvc.perform(get("/api/v1/me")
.queryParam("name", request.getName())
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn();
// 結果のassert
assertThat(result.getResponse().getContentAsString())
.isEqualTo(objectMapper.writeValueAsString(new MeResponse("nob", 13)));
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
}
service テスト作成
package nob.example.easyapp.service;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import nob.example.easyapp.repository.UsersRepository;
import nob.example.easyapp.repository.entity.Users;
import nob.example.easyapp.service.model.LoginInModel;
import nob.example.easyapp.service.model.LoginOutModel;
import nob.example.easyapp.service.model.MeInModel;
import nob.example.easyapp.service.model.MeOutModel;
/**
* AuthServiceImplのテストクラスです。
*
* @author nob
*/
@SpringBootTest
@ActiveProfiles("test")
public class AuthServiceImplTest {
@Autowired
private AuthService authService;
@MockitoBean
private UsersRepository usersRepository;
/**
* loginのテスト 正常系
*/
@Test
void test_login_success() {
// 入力値の作成
LoginInModel inModel = new LoginInModel("nob", "passwd");
// repositoryのモック化
Mockito.when(usersRepository.findByName("nob")).thenReturn(new Users("nob", "passwd", 13));
try {
// service呼び出し
LoginOutModel outModel = authService.login(inModel);
// 結果のassert
assertThat(outModel.isValid()).isTrue();
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
/**
* meのテスト 正常系
*/
@Test
void test_me_success() {
// 入力値の作成
MeInModel inModel = new MeInModel("nob");
// repositoryのモック化
Mockito.when(usersRepository.findByName("nob")).thenReturn(new Users("nob", "passwd", 13));
try {
// service呼び出し
MeOutModel outModel = authService.me(inModel);
// 結果のassert
assertThat(outModel.getName()).isEqualTo("nob");
assertThat(outModel.getAge()).isEqualTo(13);
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
}
repository テスト作成
テストデータ準備
test/resources/repository/users 配下にテストデータ投入向けの SQL を用意します:
- schema.sql
CREATE TABLE IF NOT EXISTS users(
name VARCHAR(8) PRIMARY KEY
, password VARCHAR(32)
, age INT
);
- data.sql
INSERT INTO users (
name
, password
, age
) VALUES (
'nob'
, 'passwd'
, 13
);
テストケース作成
package nob.example.easyapp.repository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import nob.example.easyapp.repository.entity.Users;
/**
* UsersRepositoryのテストクラスです。
*
* @author nob
*/
@DataJpaTest
@ActiveProfiles("test")
@TestPropertySource(properties = {
"spring.sql.init.schema-locations=classpath:/repository/users/schema.sql",
"spring.sql.init.data-locations=classpath:/repository/users/data.sql"
})
public class UsersRepositoryTest {
@Autowired
private UsersRepository usersRepository;
/**
* findByNameのテスト 正常系
*/
@Test
void test_findByName() {
try {
Users users = usersRepository.findByName("nob");
assertThat(users.getName()).isEqualTo("nob");
assertThat(users.getPassword()).isEqualTo("passwd");
assertThat(users.getAge()).isEqualTo(13);
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
}
テスト起動
# 特定のテストクラスを実行する場合
./mvnw test -Dtest=AuthControllerImplTest
# 全てのテストクラスを実行する場合
./mvnw test
カバレッジ出力
cf. https://www.jacoco.org/jacoco/trunk/doc/maven.html
設定
下記設定を追記します:
- pom.xml
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.14</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<reporting>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<reportSets>
<reportSet>
<reports>
<!-- select non-aggregate reports -->
<report>report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
カバレッジレポート作成
- カバレッジレポートを
target/site配下に出力します:
./mvnw test jacoco:report