我正在单元测试中使用
@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 也很陌生。
谢谢!
编辑:
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
}
}
您可以在使用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,这意味着正在设置一个模拟请求,它带有它自己的请求上下文(因此它有自己的请求上下文属性)。
希望这有帮助!
检查您的测试用例是否与源文件夹中的包位于同一包中,如果需要,请添加 @ComponentScan(basePackages = {required packages})