You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中Cucumber测试Mock服务失败且WebApplicationContext注入异常

解决Cucumber测试中WebApplicationContext注入失败与Mock不生效的问题

你的问题主要来自Cucumber与Spring Boot整合的生命周期不匹配,以及Mock对象未被Spring容器接管这两个关键点,下面分步给出解决方案:

一、修复WebApplicationContext注入失败的问题

1. 调整Spring与Cucumber的整合配置

Cucumber有自己的上下文加载机制,不能直接在步骤类上用JUnit的@SpringBootTest注解,需要单独创建一个Cucumber配置类:

package steps;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.web.WebAppConfiguration;
import io.cucumber.spring.CucumberContextConfiguration;

@CucumberContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@WebAppConfiguration
public class CucumberSpringConfig {
}

2. 修正步骤类的注解与生命周期方法

移除步骤类DeleteDesignationById上的@SpringBootTest、@ContextConfiguration、@WebAppConfiguration注解,同时把JUnit的@BeforeEach换成Cucumber的@Before注解(因为Cucumber场景的初始化要遵循它自己的生命周期):

// 替换导入的包
import cucumber.api.java.Before;

// ... 其他代码

@Before
public void setUp() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
        .apply(springSecurity(springSecurityFilterChain))
        .build();
}

这样Cucumber会通过配置类正确加载Spring上下文,@Autowired的WebApplicationContext就能正常注入了。

二、让DesignationServicesImpl的Mock生效

你手动创建的Mock对象并没有被Spring容器接管,接口调用时还是会用真实的服务实现,需要改用Spring Boot的@MockBean注解来替换真实Bean:

1. 替换手动Mock的代码

移除手动创建Mock的行:

// 删掉这行
private DesignationServicesImpl designationService=Mockito.mock(DesignationServicesImpl.class);

改用@MockBean注入Mock对象:

import org.springframework.boot.test.mock.mockito.MockBean;

// ... 其他代码

@MockBean
private DesignationServicesImpl designationService;

2. 调整Mock行为的设置

现在你在步骤方法里设置的Mock行为会被Spring容器识别,接口调用时会使用这个Mock实例:

@When("^I send Delete Request for Designation with its Id$")
public void i_send_Delete_Request_for_Designation_with_its_Id() throws Throwable {
    String uri = "/api/designation/1";
    // 设置Mock的返回值
    when(designationService.deleteDesignationById(1L))
            .thenReturn("deleted");

    // 直接用setUp里初始化好的mockMvc,无需重复创建
    this.mvcResult = mockMvc.perform(
            MockMvcRequestBuilders.delete(uri)
                    .header("Authorization", token))
            .andReturn();
}

三、额外的代码优化

  • 把MvcResult改成类变量,方便在Then步骤里断言响应状态:
private MvcResult mvcResult;

// ... 其他代码

@Then("^Valid http response of (\\d+) recieved$")
public void valid_http_response_of_recieved(int expectedStatus) throws Throwable {
    assertEquals(expectedStatus, mvcResult.getResponse().getStatus());
}
  • 移除header_is_with_Jwt_token_for_Delete_Request方法里的冗余代码,因为请求头可以直接在perform方法中设置。

最终的步骤类代码示例

package steps;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;

import javax.servlet.Filter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.exampletest.subpkg.employee.serviceimpls.DesignationServicesImpl;

import cucumber.api.java.Before;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;

public class DeleteDesignationById {
    String URI;
    private MvcResult mvcResult;
    String token = "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJNYW5pIiwic2VjcmV0IjpbeyJhdXRob3JpdHkiOiJST0xFX0FETUlOIn1dLCJleHAiOjE1NzQxOTIxNjYsImlhdCI6MTU3NDE3NDE2Nn0._cwqQ65_N9qL1cV79zaAsiMbR2Y0eMQFG642Evb90gs";

    @MockBean
    private DesignationServicesImpl designationService;

    @Autowired
    private WebApplicationContext webAppContext;

    @Autowired
    private Filter springSecurityFilterChain;

    private MockMvc mockMvc;

    @Before
    public void setUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
                .apply(springSecurity(springSecurityFilterChain))
                .build();
    }

    @Given("^I set service endpoint for Delete Designation$")
    public void i_set_service_endpoint_for_Delete_Designation() throws Throwable {
        URI = "http://localhost:8080/api/designation/1";
    }

    @When("^header is with Jwt token for Delete Request$")
    public void header_is_with_Jwt_token_for_Delete_Request() throws Throwable {
        // 无需单独处理,请求头在后续调用中直接设置
    }

    @When("^I send Delete Request for Designation with its Id$")
    public void i_send_Delete_Request_for_Designation_with_its_Id() throws Throwable {
        String uri = "/api/designation/1";
        when(designationService.deleteDesignationById(1L))
                .thenReturn("deleted");

        this.mvcResult = mockMvc.perform(
                MockMvcRequestBuilders.delete(uri)
                        .header("Authorization", token))
                .andReturn();
    }

    @Then("^Valid http response of (\\d+) recieved$")
    public void valid_http_response_of_recieved(int expectedStatus) throws Throwable {
        assertEquals(expectedStatus, mvcResult.getResponse().getStatus());
    }
}

内容的提问来源于stack exchange,提问作者Nikhil Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:45:02