「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」
前言
这里我们针对Spring-Web和Spring-WebMvc进行分析。
Spring源码导入后会有一个intergration-tests模块,这个模块意思是整合测试,也就是把其他模块都加入进来,做一个整合,但是没有spring-webmvc模块,这个模块测试的也不多,spring框架的单元测试主要都写在各个模块的test中。
我们进入spring-webmvc下的test,可以看到光写的单元测试,也有不少。
但是这就有个问题,spring-webmvc中有个核心Servlet,叫DispatcherServle,通常Servlet都是放在外部容器中的,但是Spring如果要编写单元测试,对这个DispatcherServle进行测试,或者其他组件,会不会启动Tomcat呢?
答案是不会,太麻烦了。
既然外部容器会调用Servlet的service方法,那我们手动调用不可以吗?
当然可以,但参数怎么办?自己new一个呗,所以在Spring中就有了Mock。
比如使用MockHttpServletRequest构造一个HttpServletRequest对象,参数指定请求的方式,还有地址。
MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest("GET","/getUser");
下面是对一个基本接口进行测试。
public class WebMvcTest {
@org.springframework.stereotype.Controller
static class UserController {
@GetMapping("getUser")
@ResponseBody
public String getUser() {
return "user";
}
}
@Test
public void test() throws ServletException, IOException {
MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest("GET", "/getUser");
MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.setServletContext(new MockServletContext());
ctx.register(UserController.class);
ctx.refresh();
DispatcherServlet servlet = new DispatcherServlet(ctx);
servlet.init(new MockServletConfig());
servlet.service(mockHttpServletRequest, mockHttpServletResponse);
}
}
最后用assertThat判断结果是不是预期值。
assertThat(mockHttpServletResponse.getContentAsString()).isEqualTo("user");
这样一个单元测试就算完成了。
在举个例子,比如手动构建一个AOP,拦截切入点为标有LogMethod注解类下的所有方法。
public class WebMvcTest {
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
static @interface LogMethod{
}
@LogMethod
@org.springframework.stereotype.Controller
public static class UserController {
@GetMapping("getUser")
@ResponseBody
public String getUser() {
return "user";
}
}
private DispatcherServlet initServlet(final Class<?> controllerClass) throws ServletException {
DispatcherServlet dispatcherServlet = new DispatcherServlet() {
@Override
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
GenericWebApplicationContext wac = new GenericWebApplicationContext();
wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerClass));
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setProxyTargetClass(true);
autoProxyCreator.setBeanFactory(wac.getBeanFactory());
wac.getBeanFactory().addBeanPostProcessor(autoProxyCreator);
Pointcut pointcut = new AnnotationMatchingPointcut(LogMethod.class);
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("进入方法"+method);
}
});
wac.getBeanFactory().registerSingleton("advisor", advisor);
wac.refresh();
return wac;
}
};
dispatcherServlet.init(new MockServletConfig());
return dispatcherServlet;
}
@Test
public void test() throws Exception {
DispatcherServlet dispatcherServlet = initServlet(UserController.class);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/getUser");
MockHttpServletResponse response = new MockHttpServletResponse();
dispatcherServlet.service(request, response);
System.out.println(response.getContentAsString());
}
}
其他也是一样。