Spring Web模块是怎么进行单元测试的?

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

前言

这里我们针对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());
   }
}

其他也是一样。

- END -


原文始发于微信公众号(十四个字节):Spring Web模块是怎么进行单元测试的?