Spring Bootでテストを書くたびに「これは @SpringBootTest でいいのか、@WebMvcTest を使うべきか」と検索し直していませんか。とりあえず全部 @SpringBootTest で書いた結果、テストの実行に数分かかるようになったプロジェクトもよく見かけますよね。
この記事では、テスト対象のレイヤーから使うべきアノテーションを即決できる早見表と判断フローをまとめます。各アノテーションの最小サンプルも載せますが、詳しい実装はそれぞれの個別記事へ誘導するハブ記事として使ってください。
まず結論の早見表
テスト対象から引ける形で整理すると、こうなります。
| アノテーション | 起動する範囲 | 速度 | 主な用途 | 併用するモック手段 |
|---|---|---|---|---|
| なし(素のJUnit) | 起動しない | 最速 | Serviceの単体テスト | Mockito |
@WebMvcTest | MVC関連Beanのみ | 速い | Controllerの入出力検証 | @MockitoBean + MockMvc |
@DataJpaTest | JPA関連Beanのみ | 速い | Repositoryのクエリ検証 | 原則モックなし |
@JsonTest | Jackson関連のみ | 最速級 | JSONシリアライズ検証 | 不要 |
@RestClientTest | クライアントBeanのみ | 速い | 外部API呼び出し検証 | MockRestServiceServer |
@SpringBootTest | 全Bean | 遅い | 複数レイヤーの結合検証 | Testcontainers / WireMock |
MongoDBなら @DataMongoTest、JDBCなら @JdbcTest というように、他のスライステストも同じ発想で用意されています。
判断フローはシンプルです。
- Serviceのロジックを検証したいなら、アノテーション不要。素のJUnit + Mockitoで書く
- Controllerなら
@WebMvcTest、Repositoryなら@DataJpaTestなどレイヤー対応のスライステストを選ぶ - 複数レイヤーを貫通する検証だけ
@SpringBootTestを使う
基本方針は「まずスライステスト、@SpringBootTest は結合テストのみ」です。
なぜ「全部@SpringBootTest」だと遅いのか
@SpringBootTest はアプリの全Beanを含むApplicationContextを起動します。DB接続やセキュリティ設定まで全部です。一方スライステストは対象レイヤーのBeanだけを起動するので、1回あたりの起動コストが大きく違います。
「でもコンテキストはキャッシュされるのでは?」と思いますよね。確かにSpringのテストフレームワークは同一設定のコンテキストを再利用します。ただしキャッシュが効くのは設定が完全に一致する場合だけで、@MockitoBean の対象が違う、@TestPropertySource でプロパティを変えた、といったテストクラスごとの差分があるとキャッシュミスになり、その都度フル起動が走ります。
つまり @SpringBootTest をあちこちで使うほど「フル起動 × キャッシュミス回数」でビルドが延びていくわけです。単一レイヤーの検証はスライステストに寄せて、この掛け算自体を小さくしましょう。
Controller層は@WebMvcTest + MockMvc
@WebMvcTest が起動するのはControllerやFilter、Jackson設定といったMVC関連のBeanだけです。ServiceやRepositoryは含まれないので、依存するServiceは @MockitoBean で差し替えます。
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
MockMvc mockMvc;
@MockitoBean
UserService userService;
@Test
void getUser_returnsOk() throws Exception {
when(userService.findById(1L))
.thenReturn(new UserResponse(1L, "alice"));
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("alice"));
}
}
リクエストの組み立てやバリデーション検証のパターンは MockMvcによるControllerテストの記事 で詳しく扱っています。
Service層はアノテーション不要
見落とされがちですが、Serviceの単体テストにSpringのアノテーションは要りません。コンテキストを一切起動せず、@ExtendWith(MockitoExtension.class) と @Mock / @InjectMocks だけで完結します。
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
UserRepository userRepository;
@InjectMocks
UserService userService;
@Test
void findById_returnsUser() {
when(userRepository.findById(1L))
.thenReturn(Optional.of(new User(1L, "alice")));
UserResponse result = userService.findById(1L);
assertThat(result.name()).isEqualTo("alice");
verify(userRepository).findById(1L);
}
}
コンテキスト起動がないので実行は一瞬です。ビジネスロジックの検証はここに寄せるのが理想ですね。when / verify の基本文法は JUnitとMockitoによるテスト入門記事 を参照してください。
Repository層は@DataJpaTest
@DataJpaTest はRepositoryや EntityManager などJPA関連のBeanだけを起動し、デフォルトでDBをインメモリDBに自動置換します。各テスト後に自動ロールバックされるので、データの後始末も不要です。
@DataJpaTest
class UserRepositoryTest {
@Autowired
TestEntityManager em;
@Autowired
UserRepository userRepository;
@Test
void findByEmail_returnsUser() {
em.persist(new User("alice", "[email protected]"));
em.flush();
Optional<User> found = userRepository.findByEmail("[email protected]");
assertThat(found).isPresent();
}
}
PostgreSQL固有のSQLなど本番同等のDBで検証したい場合は、Testcontainersと組み合わせる発展形があります。使い分けの詳細は @DataJpaTestのスライステスト記事 と Testcontainersの記事 にまとめています。
JSONの検証は@JsonTest
日付フォーマットやプロパティ命名規則など、Jackson設定の検証だけならControllerを起動する必要はありません。@JsonTest はJackson関連のBeanだけを起動する最軽量級のスライステストです。
@JsonTest
class UserResponseJsonTest {
@Autowired
JacksonTester<UserResponse> json;
@Test
void serialize_formatsDate() throws Exception {
var response = new UserResponse("alice", LocalDate.of(2026, 9, 2));
assertThat(json.write(response))
.extractingJsonPathStringValue("$.created_at")
.isEqualTo("2026-09-02");
}
}
ObjectMapperのカスタマイズが増えてきたプロジェクトでは重宝しますよ。
外部APIクライアントは@RestClientTest
RestClient や RestTemplate で外部APIを呼ぶクライアントには @RestClientTest があります。対象のクライアントBeanと MockRestServiceServer だけを起動し、実際のHTTP通信なしで応答処理を検証できます。
@RestClientTest(WeatherClient.class)
class WeatherClientTest {
@Autowired
WeatherClient weatherClient;
@Autowired
MockRestServiceServer server;
@Test
void getWeather_parsesResponse() {
server.expect(requestTo("/weather/tokyo"))
.andRespond(withSuccess("{\"condition\":\"sunny\"}",
MediaType.APPLICATION_JSON));
assertThat(weatherClient.getWeather("tokyo").condition())
.isEqualTo("sunny");
}
}
WebClientを使っている場合や、タイムアウト・リトライまで含めた検証をしたい場合は、実HTTPサーバーを立てるWireMockが向いています。WireMockによる外部APIテストの記事 で比較しています。
@SpringBootTestを使うべき場面
ここまで読むと @SpringBootTest が悪者に見えるかもしれませんが、避けるものではなく適所で使うものです。Controller→Service→Repositoryを貫通するシナリオ検証、セキュリティ設定やBean定義・プロパティ解決の検証は @SpringBootTest の出番です。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserApiIntegrationTest {
@Autowired
TestRestTemplate restTemplate;
@Test
void getUser_endToEnd() {
var response = restTemplate.getForEntity("/api/users/1", UserResponse.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
}
webEnvironment はデフォルトのMOCK(モックサーブレット環境 + MockMvc)と、実際にポートを開くRANDOM_PORT(+ TestRestTemplate)を使い分けます。実DBやKafkaを含めた結合テストの組み方は @SpringBootTestとTestcontainersによる結合テスト記事 で解説しています。結合テストの本数は絞って、日常の検証はスライステストに寄せましょう。
@MockBeanは非推奨、@MockitoBeanへ
1点注意です。長らく使われてきた @MockBean / @SpyBean は、Spring Framework 6.2(Spring Boot 3.4)で非推奨になり、Spring Framework本体の @MockitoBean / @MockitoSpyBean に置き換わりました。この記事のサンプルもすべて @MockitoBean で統一しています。
importを org.springframework.boot.test.mock.mockito.MockBean から org.springframework.test.context.bean.override.mockito.MockitoBean に変え、アノテーション名を置換すればほぼ完了しますが、細かい非互換もあります。詳しくは @MockBeanから@MockitoBeanへの移行記事 を参照してください。
ツールとの組み合わせ対応表
最後に、モック・テストツールがどのアノテーションと組み合わさるかを整理しておきます。「アノテーションがコンテキストの範囲を決め、ツールが境界の外を偽装する」と考えると覚えやすいですよ。
| テスト対象 | アノテーション | 組み合わせるツール |
|---|---|---|
| Service | なし | Mockito(when / verify) |
| Controller | @WebMvcTest | MockMvc + @MockitoBean |
| Repository | @DataJpaTest | 必要ならTestcontainers |
| JSON変換 | @JsonTest | JacksonTester |
| 外部APIクライアント | @RestClientTest | MockRestServiceServer |
| 結合テスト | @SpringBootTest | Testcontainers / WireMock |
まとめ
迷ったらこの順で考えましょう。
- テスト対象のレイヤーを特定する
- 対応するスライステスト、Serviceなら素のJUnit + Mockitoを選ぶ
- レイヤーをまたぐ検証だけ
@SpringBootTestにする
「まずスライステスト、@SpringBootTest は結合テストのみ」。この方針だけでテストの実行時間は大きく変わります。深掘りしたいレイヤーが決まったら、各セクションで紹介した個別記事へ進んでみてください。