(当接口的参数用@RequestBody修饰,同时还有另外的参数的情况)
测试接口的时候,如果项目中请求经过网关,转发到服务时,中间会将请求头数据转换成参数对象Subject。
格式如下:
@PutMapping("/demo/update")public String update(@Valid @RequestBody DemoRO demoRO, Subject subject) {
//...
return "success";
}
这时,如果使用mock测试,参数subject传递一直失败,无法解析。
经过一番查找,将实现了解析参数的 WebMvcConfigurer的实现类WebConfiguration加到Test的基类中加载,实现了正常的接口测试
@Configurationpublic class WebConfiguration implementsWebMvcConfigurer {publicWebConfiguration() {
}//...
@Beanpublic FilterRegistrationBeanglobalContextFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(new GlobalContextFilter(), new ServletRegistrationBean[0]);
registrationBean.addUrlPatterns(new String[]{"/*"});
registrationBean.setOrder(-2147483548);returnregistrationBean;
}public void addArgumentResolvers(Listresolvers) {//根据函数定义的参数类型匹配传入的参数,并转换成Subject对象
resolvers.add(newSubjectHandlerMethodArgumentResolver());
}//...
}
请求参数与接口参数匹配类
public class SubjectHandlerMethodArgumentResolver implementsHandlerMethodArgumentResolver {publicSubjectHandlerMethodArgumentResolver() {
}public booleansupportsParameter(MethodParameter parameter) {return Subject.class.isAssignableFrom(parameter.getParameterType());
}public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throwsException {
//如果接口参数满足类型匹配(即 supportsParameter == true),则获取由GlobalContext管理的存储在ThreadLocal的subjectreturn GlobalContext.getSubject().orElse((Object)null);
}}
请求拦截器GlobalContextFilter
public class GlobalContextFilter extendsOncePerRequestFilter {publicGlobalContextFilter() {
}protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throwsServletException, IOException {this.resolverSubject(request);try{
filterChain.doFilter(request, response);
}finally{this.clear();
}
}private voidclear() {
GlobalContext.clear();
}//...
private voidresolverSubject(HttpServletRequest request) {if (NumberUtils.isNumber(request.getHeader("user-Id"))) {
Long id= Long.valueOf(request.getHeader("user-Id"));
Subject subject=Subject.builder().id(id).build();//解析请求头,并存入封装了ThreadLocal的GlobalContext中
GlobalContext.setSubject(subject);
}
}
}
测试基类
@RunWith(SpringRunner.class)
@SpringBootTest(classes= DemoApplication.class, webEnvironment =SpringBootTest.WebEnvironment.DEFINED_PORT)
@ContextConfiguration(classes= WebConfiguration.class, initializers= ConfigFileApplicationContextInitializer.class)
@AutoConfigureMockMvcpublic classBaseTest {
@AutowiredprotectedMockMvc mockMvc;/*** web项目上下文*/@ResourceprivateWebApplicationContext webApplicationContext;
@Resourceprivate FilterRegistrationBeanglobalContextFilter;@Beforepublic voidbefore() {//获取mockmvc对象实例
mockMvc =MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilter(globalContextFilter.getFilter())
.build();
}
}
Controller测试类
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
public class DemoControllerTest extendsBaseTest {private final HttpHeaders httpHeaders = newHttpHeaders();
@Overridepublic voidbefore() {super.before();httpHeaders.add("user-Id", "100001");
}
@Testpublic void testUpdate() throwsException {
DemoRO updateRO= newDemoRO();updateRO.setName("Demo111");this.mockMvc.perform(
put("/demo/update")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.headers(httpHeaders)
.content(JSON.toJSONString(updateRO))
).andDo(print());
}