Skip to content

テスト標準

25.1. JUnit 5テスト作成基準

25.1.1. テストクラスネーミング

対象ネーミングルール
単体テスト{対象クラス}TestUserServiceTest
統合テスト{対象クラス}IntegrationTestUserControllerIntegrationTest
スライステスト{対象クラス}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. スライステスト

特定のレイヤーのみをロードしてテストします。

アノテーション対象レイヤー用途
@WebMvcTestControllerAPIリクエスト/レスポンス検証
@JooqTestRepositoryjOOQクエリ検証
@JsonTestJSONシリアライゼーション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秒以内に完了することを目標とします。

TIENIPIA QUALIFIED STANDARD