当控制器依赖于请求上下文时,使用 @WebMvcTest 对控制器进行单元测试不起作用

问题描述 投票:0回答:2

我正在单元测试中使用

@WebMvcTest
为我的控制器进行单元测试。我的控制器从
RequestAttributes
检索
RequestContextHolder
数据:

RequestContextHolder.currentRequestAttributes().getAttribute(name, RequestAttributes.SCOPE_REQUEST)

这里存储的数据是在拦截器中设置的:

RequestAttributes reqAttr = RequestContextHolder.currentRequestAttributes();
reqAttr.setAttribute(name, value, RequestAttributes.SCOPE_REQUEST);
RequestContextHolder.setRequestAttributes(reqAttr, true);

在我的控制器单元测试中,拦截器未运行,因此我尝试在运行测试之前设置请求属性,但收到此错误:

java.lang.IllegalStateException:未找到线程绑定请求:您是指实际 Web 请求之外的请求属性,还是在原始接收线程之外处理请求?如果您实际上在 Web 请求中进行操作并且仍然收到此消息,则您的代码可能在 DispatcherServlet 之外运行:在这种情况下,请使用 RequestContextListener 或 RequestContextFilter 来公开当前请求。

我已经尝试过这个答案https://stackoverflow.com/a/9419859/5545327。我补充道:

@Mock
private RequestAttributes attrs;

@Before
public void before() {
    MockitoAnnotations.initMocks(this);
    RequestContextHolder.setRequestAttributes(attrs);
}

@Test
public void testController() {
    when(requestAttributes.getAttribute(eq("<attribute name>"),
        eq(RequestAttributes.SCOPE_REQUEST))).thenReturn(<mockReturn>);
    // do your test...
}

但是它不起作用。错误消失了,但控制器正在从

null
获取
RequestContextHolder
值。顺便说一句,我正在通过这样做来测试我的控制器:

mockMvc.perform(post("<url>").content("<post params here>")).andExpect(status().isOk());

其中

mockMvc
来自
@WebMvcTest

注意: 我有一个不同的类用于集成测试,这就是为什么我在这里使用

@WebMvcTest
而不是
@SpringBootTest

我希望你能给出一个描述性的答案,因为我对 Spring 也很陌生。

谢谢!

编辑:

  • 我不能简单地将数据放入实际的 HttpRequest 属性中。如果我没记错的话,HttpRequest 属性不会自动添加为
    RequestContextHolder
    中的请求属性。另外,我需要的数据不是来自HttpRequest,而是基于HttpRequest。我嘲笑它,因为我知道实际的请求,因为这是我的单元测试。

代码如下所示:

@RunWith(SpringRunner.class)
@WebMvcTest
@OverrideAutoConfiguration(enabled = true)
@ActiveProfiles("test")
public abstract class BaseControllerTest {
    @Autowired
    protected MockMvc mockMvc;
}

public class ControllerTest extends BaseControllerTest {
    @Test
    public void testControllerMethod() {
        mockMvc.perform(post("<url>").content("<post params here>")).andExpect(status().isOk());
        // some more codes here
    }
}

public class Controller {
    @PostMapping
    public String controllerMethod() {
        // some more codes here
        Object myObject = RequestContextHolder.currentRequestAttributes().getAttribute(name, RequestAttributes.SCOPE_REQUEST);
        // some more codes here
    }
}
java spring unit-testing spring-mvc controller
2个回答
0
投票

您可以在使用mockMvc进行调用时设置请求属性。

根据您的测试代码,您可以将其修改为:

public class ControllerTest extends BaseControllerTest {
    @Test
    public void testControllerMethod() {
        mockMvc.perform(post("<url>")
            .content("<post params here>"))
            .requestAttr("my-request-attribute", "some-value")
            .andExpect(status().isOk());
        // some more codes here
    }
}



public class Controller {
    @PostMapping
    public String controllerMethod() {
        // some more codes here
        Object myObject = RequestContextHolder
            .currentRequestAttributes()
            .getAttribute("my-request-attribute", RequestAttributes.SCOPE_REQUEST);
        // some more codes here
    }
}

我认为解决这个问题的第一种方法(即直接设置 RequestContextHolder 的属性)仅适用于没有实际请求发生的情况(例如测试服务层)。

在你的例子中,由于我们在这里处理模拟MVC,这意味着正在设置一个模拟请求,它带有它自己的请求上下文(因此它有自己的请求上下文属性)。

希望这有帮助!


0
投票

检查您的测试用例是否与源文件夹中的包位于同一包中,如果需要,请添加 @ComponentScan(basePackages = {required packages})

© www.soinside.com 2019 - 2024. All rights reserved.