如何对Spring MVC注释控制器进行单元测试?

20

我正在跟随一个Spring 2.5教程,同时尝试将代码/设置更新为Spring 3.0。

Spring 2.5 中,我有一个HelloController(供参考):

public class HelloController implements Controller {
    protected final Log logger = LogFactory.getLog(getClass());
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        logger.info("Returning hello view");
        return new ModelAndView("hello.jsp");
    }
}

以下是对 HelloController 进行 JUnit 测试的参考代码:

public class HelloControllerTests extends TestCase {
    public void testHandleRequestView() throws Exception{
        HelloController controller = new HelloController();
        ModelAndView modelAndView = controller.handleRequest(null, null);
        assertEquals("hello", modelAndView.getViewName());
    }
}

但是现在我将控制器更新到了Spring 3.0,并且它现在使用注释(我还添加了一个消息):

@Controller
public class HelloController {
    protected final Log logger = LogFactory.getLog(getClass());
    @RequestMapping("/hello")
    public ModelAndView handleRequest() {
        logger.info("Returning hello view");
        return new ModelAndView("hello", "message", "THIS IS A MESSAGE");
    }
}

我正在使用JUnit 4.9,有人能够解释一下如何对这个最后一个控制器进行单元测试吗?


3个回答

25

基于注解的Spring MVC的一个优点是可以直接进行测试,例如:

import org.junit.Test;
import org.junit.Assert;
import org.springframework.web.servlet.ModelAndView;

public class HelloControllerTest {
   @Test
   public void testHelloController() {
       HelloController c= new HelloController();
       ModelAndView mav= c.handleRequest();
       Assert.assertEquals("hello", mav.getViewName());
       ...
   }
}

这种方法有什么问题吗?

对于更高级的集成测试,Spring文档中有一个org.springframework.mock.web参考资料


+1 谢谢你,Sasha。它运行得很好。我无法想象它是如此简单的。 - nunaxe
如果HelloController中有@Autowired组件,这将无法正常工作。 - Aram Kocharyan
@AramKocharyan:在单元测试中,我建议不要使用@Autowired注解,而是通过构造函数或setter方法显式地提供依赖项。如果你真的想要享受Spring框架的所有好处,可以参考http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java。 - Sasha O
@SashaO 嗯,我这周学到了那个:P 我只是按照你说的用setter设置了autowired实例,效果很好。 - Aram Kocharyan
我认为这种方法太过于白盒化。视图被称为“hello”并不意味着它正在执行所需的操作。我们正在使用HttpClient对控制器进行单元测试,就像测试浏览器一样。 - Rafael
@Rafael -- 我理解你为什么想使用HttpClient测试View(顺便说一句,http://htmlunit.sourceforge.net/非常适合这个任务),但是你为什么要测试Controller呢?对于Controller,你需要测试它是否将正确的内容放入模型并调用正确的视图。 - Sasha O

21

使用mvc:annotation-driven需要2个步骤:首先通过HandlerMapping将请求解析到处理程序,然后通过HandlerAdapter执行该处理程序的方法。类似于以下内容:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("yourContext.xml")
public class ControllerTest {

    @Autowired
    private RequestMappingHandlerAdapter handlerAdapter;

    @Autowired
    private RequestMappingHandlerMapping handlerMapping;

    @Test
    public void testController() throws Exception {
        MockHttpServletRequest request = new MockHttpServletRequest();
        // request init here

        MockHttpServletResponse response = new MockHttpServletResponse();
        Object handler = handlerMapping.getHandler(request).getHandler();
        ModelAndView modelAndView = handlerAdapter.handle(request, response, handler);

        // modelAndView and/or response asserts here
    }
}

这适用于Spring 3.1,但我猜每个版本都有其变体。看看Spring 3.0的代码,我觉得DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter应该可以解决问题。


1
我在 Object handler = handlerMapping.getHandler(request).getHandler(); 中得到了空指针异常,请问应该如何解决? - jackyesind
@jackyesind,如答案所建议,请求必须被初始化。可以使用类似 new MockHttpServletResponse("GET", "/your/uri"); 的方式进行初始化。 - mks-d

1

您也可以尝试其他与Spring无关的Web测试框架,例如HtmlUnitSelenium。除了Sasha所描述的内容之外,使用JUnit单独进行断言时不会找到更强大的策略,但您肯定应该对模型进行断言。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接