package com.example.testingweb;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HomeController {
@RequestMapping("/")
public @ResponseBody String greeting() {
return "Hello, World";
}
}
MockMvc と @MockBean で Web レイヤーテスト
このガイドでは、Spring アプリケーションを作成し、それを JUnit でテストするプロセスを説明します。
構築するもの
単純な Spring アプリケーションを構築し、JUnit でテストします。アプリケーション内の個々のクラスの単体テストを記述して実行する方法をすでに知っている可能性があるため、このガイドでは、Spring Test および Spring Boot 機能を使用して Spring とコード間の相互作用をテストすることに集中します。アプリケーションコンテキストが正常に読み込まれるという簡単なテストから始めて、Spring の MockMvc
を使用して Web レイヤーのみをテストし続けます。
必要なもの
約 15 分
Eclipse STS や IntelliJ IDEA のような任意の IDE または VSCode のようなテキストエディター
Java 1.8 以降
コードを直接 IDE にインポートすることもできます。
本ガイドの完成までの流れ
ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。
最初から始めるには、Spring Initializr から開始に進みます。
基本をスキップするには、次の手順を実行します。
このガイドを Eclipse で「Spring 入門コンテンツのインポート」するか、ソースリポジトリをダウンロードして解凍、または、Git (英語) を使用してクローンを作成します。
git clone https://github.com/spring-guides/gs-testing-web.git
gs-testing-web/initial
に cdシンプルなアプリケーションを作成するにジャンプしてください。
完了したときは、gs-testing-web/complete
のコードに対して結果を確認できます。
Spring Initializr から開始
IDE を使用する場合はプロジェクト作成ウィザードを使用します。IDE を使用せずにコマンドラインなどで開発する場合は、この事前に初期化されたプロジェクトからプロジェクトを ZIP ファイルとしてダウンロードできます。このプロジェクトは、このチュートリアルの例に合うように構成されています。
プロジェクトを手動で初期化するには:
IDE のメニューまたはブラウザーから Spring Initializr を開きます。アプリケーションに必要なすべての依存関係を取り込み、ほとんどのセットアップを行います。
Gradle または Maven のいずれかと、使用する言語を選択します。このガイドは、Java を選択したことを前提としています。
依存関係をクリックして、Spring Web を選択します。
生成をクリックします。
結果の ZIP ファイルをダウンロードします。これは、選択して構成された Web アプリケーションのアーカイブです。
Eclipse や IntelliJ のような IDE は新規プロジェクト作成ウィザードから Spring Initializr の機能が使用できるため、手動での ZIP ファイルのダウンロードやインポートは不要です。 |
プロジェクトを Github からフォークして、IDE または他のエディターで開くこともできます。 |
シンプルなアプリケーションを作成する
Spring アプリケーション用の新しいコントローラーを作成します。次のリスト(src/main/java/com/example/testingweb/HomeController.java
から)は、その方法を示しています。
前の例では、GET と PUT 、POST などを指定していません。デフォルトでは、@RequestMapping はすべての HTTP 操作をマップします。@GetMapping または @RequestMapping(method=GET) を使用して、このマッピングを絞り込むことができます。 |
アプリケーションの実行
Spring Initializr は、アプリケーションクラス(main()
メソッドを持つクラス)を作成します。このガイドでは、このクラスを変更する必要はありません。次のリスト(src/main/java/com/example/testingweb/TestingWebApplication.java
から)は、Spring Initializr が作成したアプリケーションクラスを示しています。
package com.example.testingweb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TestingWebApplication {
public static void main(String[] args) {
SpringApplication.run(TestingWebApplication.class, args);
}
}
@SpringBootApplication
は、次のすべてを追加する便利なアノテーションです。
@Configuration
: アプリケーションコンテキストの Bean 定義のソースとしてクラスにタグを付けます。@EnableAutoConfiguration
: クラスパス設定、他の Bean、さまざまなプロパティ設定に基づいて Bean の追加を開始するよう Spring Boot に指示します。@EnableWebMvc
: アプリケーションを Web アプリケーションとしてフラグを立て、DispatcherServlet
のセットアップなどの主要な動作をアクティブにします。Spring Boot は、クラスパスでspring-webmvc
を検出すると自動的に追加します。@ComponentScan
: アノテーション付きTestingWebApplication
クラス (com.example.testingweb
) が存在するパッケージ内の他のコンポーネント、構成、サービスを検索するように Spring に指示し、com.example.testingweb.HelloController
を見つけさせます。
main()
メソッドは、Spring Boot の SpringApplication.run()
メソッドを使用してアプリケーションを起動します。XML の単一行がないことに気づきましたか? web.xml
ファイルもありません。この Web アプリケーションは 100% 純粋な Java であり、接続機能やインフラストラクチャの構成に対処する必要はありませんでした。Spring Boot がすべてを処理します。
ロギング出力が表示されます。サービスは数秒以内に起動して実行されるはずです。
アプリケーションをテストする
アプリケーションが実行されたため、テストできます。http://localhost:8080
でホームページをロードできます。ただし、変更を加えたときにアプリケーションが機能するという自信を高めるために、テストを自動化する必要があります。
Spring Boot は、アプリケーションのテストを計画していると想定しているため、ビルドファイル(build.gradle または pom.xml )に必要な依存関係を追加します。 |
最初にできることは、アプリケーションコンテキストを開始できない場合に失敗する単純な健全性チェックテストを記述することです。次のリスト(src/test/java/com/example/testingweb/TestingWebApplicationTest.java
から)は、その方法を示しています。
package com.example.testingweb;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class TestingWebApplicationTests {
@Test
void contextLoads() {
}
}
@SpringBootTest
アノテーションは、Spring Boot にメイン構成クラス(たとえば @SpringBootApplication
を持つもの)を探し、それを使用して Spring アプリケーションコンテキストを開始するように指示します。このテストは、IDE またはコマンドライン(./mvnw test
または ./gradlew test
を実行することで)で実行でき、パスするはずです。コンテキストがコントローラーを作成していることを確信させるために、次の例(src/test/java/com/example/testingweb/SmokeTest.java
から)が示すように、アサーションを追加できます。
package com.example.testingweb;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SmokeTest {
@Autowired
private HomeController controller;
@Test
void contextLoads() throws Exception {
assertThat(controller).isNotNull();
}
}
Spring は @Autowired
アノテーションを解釈し、テストメソッドが実行される前にコントローラーが挿入されます。AssertJ (英語) (assertThat()
およびその他のメソッドを提供)を使用して、テストアサーションを表現します。
Spring Test サポートの優れた機能は、アプリケーションコンテキストがテスト間でキャッシュされることです。そうすれば、テストケースに複数のメソッドがある場合、同じ構成の複数のテストケースがある場合、アプリケーションを 1 回だけ起動するコストが発生します。@DirtiesContext (Javadoc) アノテーションを使用してキャッシュを制御できます。 |
健全性チェックがあると便利ですが、アプリケーションの動作をアサートするテストも作成する必要があります。これを行うには、アプリケーションを起動して接続をリッスンし(運用環境で行うように)、HTTP リクエストを送信してレスポンスをアサートします。次のリスト(src/test/java/com/example/testingweb/HttpRequestTest.java
から)は、その方法を示しています。
package com.example.testingweb;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.web.server.LocalServerPort;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class HttpRequestTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
void greetingShouldReturnDefaultMessage() throws Exception {
assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/",
String.class)).contains("Hello, World");
}
}
webEnvironment=RANDOM_PORT
を使用してランダムポートでサーバーを起動することに注意してください(テスト環境での競合を回避するのに便利です)および @LocalServerPort
でポートを挿入します。また、Spring Boot が自動的に TestRestTemplate
を提供していることに注意してください。@Autowired
を追加するだけです。
もう 1 つの便利なアプローチは、サーバーをまったく起動せず、Spring が受信 HTTP リクエストを処理してコントローラーに渡す、そのレイヤーのみをテストすることです。こうすることで、ほぼすべてのフルスタックが使用され、実際の HTTP リクエストを処理している場合とまったく同じ方法でコードが呼び出されますが、サーバーを起動するコストはかかりません。これを行うには、Spring の MockMvc
を使用し、テストケースの @AutoConfigureMockMvc
アノテーションを使用してそれを挿入するように依頼します。次のリスト ( src/test/java/com/example/testingweb/TestingWebApplicationTest.java
から) は、その方法を示しています。
package com.example.testingweb;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
@SpringBootTest
@AutoConfigureMockMvc
class TestingWebApplicationTest {
@Autowired
private MockMvc mockMvc;
@Test
void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello, World")));
}
}
このテストでは、完全な Spring アプリケーションコンテキストが開始されますが、サーバーはありません。次のリスト(src/test/java/com/example/testingweb/WebLayerTest.java
から)が示すように、@WebMvcTest
を使用して、テストを Web レイヤーのみに絞り込むことができます。
@WebMvcTest
include::complete/src/test/java/com/example/testingweb/WebLayerTest.java
テストアサーションは、前のケースと同じです。ただし、このテストでは、Spring Boot はコンテキスト全体ではなく Web レイヤーのみをインスタンス化します。複数のコントローラーを備えたアプリケーションでは、たとえば @WebMvcTest(HomeController.class)
を使用して、1 つのみのインスタンス化を要求することもできます。
これまでのところ、HomeController
は単純であり、依存関係はありません。あいさつを保存するための追加コンポーネントを導入することで、より現実的にすることができます(おそらく新しいコントローラーに)。次の例(src/main/java/com/example/testingweb/GreetingController.java
から)は、その方法を示しています。
package com.example.testingweb;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class GreetingController {
private final GreetingService service;
public GreetingController(GreetingService service) {
this.service = service;
}
@RequestMapping("/greeting")
public @ResponseBody String greeting() {
return service.greet();
}
}
次に、次のリスト(src/main/java/com/example/testingweb/GreetingService.java
から)が示すように、グリーティングサービスを作成します。
package com.example.testingweb;
import org.springframework.stereotype.Service;
@Service
public class GreetingService {
public String greet() {
return "Hello, World";
}
}
Spring は、コンストラクターのシグネチャーのために、コントローラーにサービスの依存関係を自動的に挿入します。次のリスト(src/test/java/com/example/testingweb/WebMockTest.java
から)は、このコントローラーを @WebMvcTest
でテストする方法を示しています。
package com.example.testingweb;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
@WebMvcTest(GreetingController.class)
class WebMockTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private GreetingService service;
@Test
void greetingShouldReturnMessageFromService() throws Exception {
when(service.greet()).thenReturn("Hello, Mock");
this.mockMvc.perform(get("/greeting")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello, Mock")));
}
}
@MockBean
を使用して GreetingService
のモックを作成および注入し(そうしないと、アプリケーションコンテキストを開始できません)、Mockito
を使用してその期待値を設定します。
要約
おめでとう! Spring アプリケーションを開発し、JUnit および Spring MockMvc
でテストし、Spring Boot を使用して Web レイヤーを分離し、特別なアプリケーションコンテキストをロードしました。
関連事項
次のガイドも役立ちます。
新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください [GitHub] (英語) 。
すべてのガイドは、コード用の ASLv2 ライセンス、およびドキュメント用の Attribution、NoDerivatives creative commons ライセンス (英語) でリリースされています。 |