テスト標準
25.1. JUnit 5テスト作成基準
25.1.1. テストクラスネーミング
| 対象 | ネーミングルール | 例 |
|---|---|---|
| 単体テスト | {対象クラス}Test | UserServiceTest |
| 統合テスト | {対象クラス}IntegrationTest | UserControllerIntegrationTest |
| スライステスト | {対象クラス}Test + アノテーションで区別 | UserControllerTest(@WebMvcTest) |
25.1.2. テストメソッドネーミング
日本語記述形式のメソッド名を許容します。テストの意図が明確に伝わらなければなりません。
java
@Test
void イメールでユーザーを照会する() {
// ...
}
@Test
void 存在しないユーザー照会時に例外が発生する() {
// ...
}25.1.3. Given-When-Thenパターン
すべてのテストはGiven-When-Then構造で作成します。
java
@Test
void イメールでユーザーを照会する() {
// given
String email = "user@tienipia.com";
User expected = new User(1L, "홍길동", email);
given(userRepository.findByEmail(email)).willReturn(Optional.of(expected));
// when
User result = userService.findByEmail(email);
// then
assertThat(result.getName()).isEqualTo("홍길동");
assertThat(result.getEmail()).isEqualTo(email);
}25.1.4. Assertionライブラリ
- AssertJを標準として使用します。
- JUnit 5標準の
Assertionsも使用可能ですが、AssertJのfluent APIを推奨します。
java
// AssertJ(推奨)
assertThat(result).isNotNull();
assertThat(result.getName()).isEqualTo("홍길동");
assertThat(users).hasSize(3).extracting("name").contains("홍길동");
// 例外検証
assertThatThrownBy(() -> userService.findById(999L))
.isInstanceOf(UserNotFoundException.class)
.hasMessageContaining("999");25.2. テスト分類
25.2.1. 単体テスト
- 単一クラスまたはメソッドを分離してテストします。
- 外部依存関係はMockitoでモック化します。
- Springコンテキストはロードしません。
java
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserServiceImpl userService;
@Test
void ユーザーを作成する() {
// given
CreateUserRequest request = new CreateUserRequest("홍길동", "user@tienipia.com");
given(userRepository.insert(request.name(), request.email()))
.willReturn(UsersRecordFixture.create(1L, "홍길동", "user@tienipia.com"));
// when
UserResponse response = userService.createUser(request);
// then
assertThat(response.name()).isEqualTo("홍길동");
then(userRepository).should().insert(request.name(), request.email());
}
}25.2.2. スライステスト
特定のレイヤーのみをロードしてテストします。
| アノテーション | 対象レイヤー | 用途 |
|---|---|---|
@WebMvcTest | Controller | APIリクエスト/レスポンス検証 |
@JooqTest | Repository | jOOQクエリ検証 |
@JsonTest | JSONシリアライゼーション | DTO変換検証 |
java
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void ユーザー一覧を照会する() throws Exception {
given(userService.findAll())
.willReturn(List.of(new UserResponse(1L, "홍길동", "user@tienipia.com")));
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].name").value("홍길동"));
}
}25.2.3. Repositoryテスト
jOOQベースのRepositoryテストは @JooqTest または @SpringBootTest + Testcontainersを使用します。
@JooqTest 使用例:
java
@JooqTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class UserRepositoryTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine")
.withDatabaseName("flowin_test")
.withUsername("test")
.withPassword("test");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
private DSLContext dsl;
private UserRepository userRepository;
@BeforeEach
void setUp() {
userRepository = new UserRepository(dsl);
}
@Test
void ユーザーを作成して照会する() {
// given
userRepository.insert("홍길동", "user@tienipia.com");
// when
Optional<UsersRecord> result = userRepository.findByEmail("user@tienipia.com");
// then
assertThat(result).isPresent();
assertThat(result.get().getName()).isEqualTo("홍길동");
}
}- TestcontainersがPostgrSQLコンテナを実際に起動するため、PostgreSQL固有の機能(インデックス、型など)を正確にテストすることができます。
- Flywayマイグレーションが自動的に適用され、テストDBが初期化されます。
25.2.4. 統合テスト
@SpringBootTestでコンテキスト全体をロードします。- 実際のデータベースの代わりにTestcontainers PostgreSQLを使用します。H2は使用しません。
- 統合テストはCIで必ず実行します。
25.3. カバレッジ基準
25.3.1. 最低カバレッジ
| 測定基準 | 最低比率 |
|---|---|
| ラインカバレッジ | 80% 以上 |
| ブランチカバレッジ | 70% 以上 |
25.3.2. 除外対象
以下の項目はカバレッジ測定から除外します。
- Configurationクラス(
*Config.java) - DTO(単純データクラス)
- Mainクラス(
*Application.java) - jOOQ自動生成コード(
jooq/パッケージ)
JaCoCoでの除外設定:
xml
<configuration>
<excludes>
<exclude>**/*Config.*</exclude>
<exclude>**/*Application.*</exclude>
<exclude>**/dto/**</exclude>
<exclude>**/jooq/**</exclude>
</excludes>
</configuration>25.3.3. カバレッジレポート
- JaCoCoがHTMLレポートを
target/site/jacoco/に生成します。 - CIでレポートをアーティファクトとして保存し、レビュー時に参照します。
25.4. テストデータ管理
25.4.1. テストフィクスチャ
- テストデータの生成にはファクトリメソッドまたはビルダーパターンを使用します。
- テスト間のデータ共有は禁止します。各テストは独立して実行されなければなりません。
java
class UserFixture {
public static User createDefaultUser() {
return new User(1L, "홍길동", "user@tienipia.com");
}
public static User createUser(String name, String email) {
return new User(null, name, email);
}
}25.4.2. テスト原則
- テストは独立的でなければなりません。実行順序に依存しません。
- テストは再現可能でなければなりません。外部状態に依存しません。
- テストは高速に実行されなければなりません。単体テスト全体が30秒以内に完了することを目標とします。