知識庫 / Spring RSS 訂閱

使用 SpringJUnit4ClassRunner 並使用參數化

Spring,Testing
HongKong
12
01:22 PM · Dec 06 ,2025

1. 概述

在本教程中,我們將學習如何使用 JUnit4 和 參數化測試運行器 對 Spring 集成測試進行參數化,該測試在 JUnit4 中實現。

2. SpringJUnit4ClassRunner

SpringJUnit4ClassRunner 是 JUnit4 的 ClassRunner 的一個實現,它將 Spring 的 TestContextManager 嵌入到 JUnit 測試中。

TestContextManager 是 Spring 的 TestContext 框架的入口點,因此它管理了 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 接受用户名作為路徑參數,並返回相應的用户角色。

現在,為了使用不同的用户名/角色組合測試此 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 測試。

3. 使用參數化

3.1. 定義參數

參數化 是一個自定義 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;
    }

    //...
}

如上所示,我們使用了<em>@Parameters</em>註解來準備注入到 JUnit 測試中的輸入參數。我們還為<em>@Parameter</em>字段的<em>name</em>和<em>role</em>提供了映射。

但是現在,我們面臨另一個需要解決的問題——<strong>JUnit 不允許在一個 JUnit 測試類中包含多個運行者</strong>。這意味着<strong>我們無法利用<em>SpringJUnit4ClassRunner</em>來嵌入<em>TestContextManager</em>到我們的測試類中。我們需要找到另一種嵌入<em>TestContextManager</em>的方法。

幸運的是,Spring 提供了幾種實現此目的的選項。我們在後續部分將討論這些選項。

3.2. 手動初始化 TestContextManager

第一個選項相當簡單,因為 Spring 允許我們手動初始化 TestContextManager

@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。接下來,我們在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 將會執行此測試用例兩次 — 針對我們使用 @Parameters 註解定義的每個輸入集。

3.3. <em >SpringClassRule</em ><em >SpringMethodRule</em >

通常,不建議手動初始化 <em >TestContextManager</em >。相反,Spring 建議使用 <em >SpringClassRule</em ><em >SpringMethodRule</em >

<em >SpringClassRule</em > 實現了 JUnit 的 <em >TestRule</em >——一種替代方案來編寫測試用例。<em >TestRule</em > 可用於替換以前使用@Before<em >@BeforeClass</em >、@After 和 `@AfterClass 方法進行的設置和清理操作。

<em >SpringClassRule</em ><em >TestContextManager</em > 的類級別功能嵌入到 JUnit 測試類中。它初始化 <em >TestContextManager</em > 並調用 Spring <em >TestContext</em > 的設置和清理。因此,它提供依賴注入和訪問ApplicationContext 的功能。

除了 <em >SpringClassRule</em > 之外,我們還需要使用 <em >SpringMethodRule</em >,它為 `TestContextManager 提供實例級別和方法級別的功能。

<em >SpringMethodRule</em > 負責測試方法的準備。它還檢查標記為跳過的測試用例,並阻止它們運行。

@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. 結論

在本文中,我們探討了使用 Parameterized 測試運行器代替 SpringJUnit4ClassRunner 的兩種方式來實現 Spring 集成測試的方法。我們看到了如何手動初始化 TestContextManager,並提供了一個使用 SpringClassRuleSpringMethodRule 的示例,該方法是 Spring 推薦的方法。

儘管我們僅在本文中討論了 Parameterized 運行器,但 實際上,我們可以使用這些方法與任何 JUnit 運行器一起編寫 Spring 集成測試

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.