パラメータ化されたSpringJUnit4ClassRunnerの使用

1. 概要

このチュートリアルでは、JUnit4に実装されたlink:/spring-tests[Spring integration test]を_Parameterized_ JUnitテストランナーでパラメーター化する方法を説明します。

2.  SpringJUnit4ClassRunner

__SpringJUnit4ClassRunner __は、JUnit4の__ClassRunner __thatの実装です* Springの__TestContextManager ___をJUnitテストに埋め込みます*。
__TestContextManager __は、Spring __TestContext __frameworkへのエントリポイントであるため、JUnitテストクラスでのSpring __ApplicationContext __および依存性注入へのアクセスを管理します。 したがって、_SpringJUnit4ClassRunner_を使用すると、開発者はコントローラーやリポジトリなどのSpringコンポーネントの統合テストを実装できます。
たとえば、_RestController_の統合テストを実装できます。
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = WebConfig.class)
public class RoleControllerIntegrationTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    private static final String CONTENT_TYPE = "application/text;charset=ISO-8859-1";

    @Before
    public void setup() throws Exception {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    public void givenEmployeeNameJohnWhenInvokeRoleThenReturnAdmin() throws Exception {
        this.mockMvc.perform(MockMvcRequestBuilders
          .get("/role/John"))
          .andDo(print())
          .andExpect(MockMvcResultMatchers.status().isOk())
          .andExpect(MockMvcResultMatchers.content().contentType(CONTENT_TYPE))
          .andExpect(MockMvcResultMatchers.content().string("ADMIN"));
    }
}
テストからわかるように、__ Controller ___acceptはパスパラメーターとしてユーザー名を受け入れ、それに応じてユーザーロールを返します。
ここで、*このRESTサービスを異なるユーザー名/ロールの組み合わせでテストするには、新しいテストを実装する必要があります:*
@Test
public void givenEmployeeNameDoeWhenInvokeRoleThenReturnEmployee() throws Exception {
    this.mockMvc.perform(MockMvcRequestBuilders
      .get("/role/Doe"))
      .andDo(print())
      .andExpect(MockMvcResultMatchers.status().isOk())
      .andExpect(MockMvcResultMatchers.content().contentType(CONTENT_TYPE))
      .andExpect(MockMvcResultMatchers.content().string("EMPLOYEE"));
}
これは、多数の入力の組み合わせが可能なサービスではすぐに手に負えなくなる可能性があります。*
テストクラスでこの種の繰り返しを避けるために、複数の入力を受け入れるJUnitテストを実装するために__Parameterized __を使用する方法を見てみましょう。

3. Parameter_Parameterized_の使用

3.1. パラメータの定義

_Parameterized_は、単一のテストケースを記述し、複数の入力パラメーターに対して実行させるカスタムJUnitテストランナーです。
@RunWith(Parameterized.class)
@WebAppConfiguration
@ContextConfiguration(classes = WebConfig.class)
public class RoleControllerParameterizedIntegrationTest {

    @Parameter(value = 0)
    public String name;

    @Parameter(value = 1)
    public String role;

    @Parameters
    public static Collection<Object[]> data() {
        Collection<Object[]> params = new ArrayList();
        params.add(new Object[]{"John", "ADMIN"});
        params.add(new Object[]{"Doe", "EMPLOYEE"});

        return params;
    }

    //...
}
上記のように、_ @ Parameters_アノテーションを使用して、JUnitテストに注入する入力パラメーターを準備しました。 また、values __ @ Parameter the__fields __name __and _role._でこれらの値のマッピングを提供しました。
しかし、今、解決する別の問題があります-* JUnitでは、1つのJUnitテストクラスで複数のランナーを許可しません*。 つまり、**テストクラスに** __ * TestContextManager * ___を埋め込むために_SpringJUnit4ClassRunner_を利用することはできません。 _TestContextManager_を埋め込む別の方法を見つける必要があります。
幸いなことに、Springはこれを実現するためのオプションをいくつか提供しています。 これらについては、次のセクションで説明します。

3.2. _TestContextManager_を手動で初期化する

Springでは__TestContextManager simple__manuallyを初期化できるため、最初のオプションは非常に簡単です。
@RunWith(Parameterized.class)
@WebAppConfiguration
@ContextConfiguration(classes = WebConfig.class)
public class RoleControllerParameterizedIntegrationTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    private TestContextManager testContextManager;

    @Before
    public void setup() throws Exception {
        this.testContextManager = new TestContextManager(getClass());
        this.testContextManager.prepareTestInstance(this);

        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    //...
}
特に、この例では、_SpringJUnit4ClassRunner._の代わりに__Parameterized __runnerを使用しました。次に、_setup()_メソッドで___TestContextManager __を初期化しました。
これで、パラメーター化されたJUnitテストを実装できます。
@Test
public void givenEmployeeNameWhenInvokeRoleThenReturnRole() throws Exception {
    this.mockMvc.perform(MockMvcRequestBuilders
      .get("/role/" + name))
      .andDo(print())
      .andExpect(MockMvcResultMatchers.status().isOk())
      .andExpect(MockMvcResultMatchers.content().contentType(CONTENT_TYPE))
      .andExpect(MockMvcResultMatchers.content().string(role));
}
  • JUnitはこのテストケースを2回実行します* _ @ Parameters_アノテーションを使用して定義した入力のセットごとに1回実行します。

3.3. SpringClassRule_および_SpringMethodRule

通常、* TestContextManager_を手動で初期化することはお勧めできません*。 代わりに、Springは_SpringClassRule_および_SpringMethodRule._の使用を推奨しています。
_SpringClassRule_は、JUnitの_TestRuleを実装します—テストケースを記述する別の方法です。 _TestRule_を使用して、以前に_ @ Before、fore @ BeforeClass、@ After、_、および__ @ AfterClassmethod__methodsで実行されたセットアップおよびクリーンアップ操作を置き換えることができます。
_SpringClassRule_は、JUnitテストクラスに__TestContextManager __のクラスレベルの機能を埋め込みます。 __TestContextManager __を初期化し、Spring _TestContext._のセットアップとクリーンアップを呼び出します。したがって、依存関係の注入と_ApplicationContext_へのアクセスを提供します。
_SpringClassRule_に加えて、_SpringMethodRule_も使用する必要があります。 _TestContextManager._のインスタンスレベルおよびメソッドレベルの機能を提供します
__SpringMethodRule __は、テストメソッドの準備を担当します。 また、スキップするようにマークされているテストケースをチェックし、実行されないようにします。
テストクラスでこのアプローチを使用する方法を見てみましょう。
@RunWith(Parameterized.class)
@WebAppConfiguration
@ContextConfiguration(classes = WebConfig.class)
public class RoleControllerParameterizedClassRuleIntegrationTest {
    @ClassRule
    public static final SpringClassRule scr = new SpringClassRule();

    @Rule
    public final SpringMethodRule smr = new SpringMethodRule();

    @Before
    public void setup() throws Exception {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    //...
}

4. 結論

この記事では、_SpringJUnit4ClassRunner_の代わりに_Parameterized_テストランナーを使用してSpring統合テストを実装する2つの方法について説明しました。 __TestContextManager __manuallyを初期化する方法を見て、Springが推奨するアプローチである_SpringMethodRule_で_SpringClassRule_を使用する例をみました。
この記事では__Parameterized __runnerについてのみ説明しましたが、*これらのアプローチのいずれかを任意のJUnitランナーで実際に使用して、Spring統合テストを作成できます。
いつものように、すべてのサンプルコードはhttps://github.com/eugenp/tutorials/tree/master/testing-modules/spring-testing[GitHub]で入手できます。