单元测试Java Servlet

时间:2020-03-06 14:20:24  来源:igfitidea点击:

我想知道什么是对Servlet进行单元测试的最佳方法。

只要测试内部方法不引用servlet上下文,就不会有问题,但是如何测试doGet / doPost方法以及引用该上下文或者使用会话参数的内部方法呢?

有没有一种方法可以简单地使用经典工具(例如JUnit或者最好是TestNG)来做到这一点?我是否需要嵌入tomcat服务器或者类似的东西?

解决方案

尝试使用HttpUnit,尽管我们最终可能会写自动化测试,该测试比模块的"单元测试"(单个类的)更"集成测试"。

我们是否在单元测试中手动调用doPost和doGet方法?如果是这样,我们可以重写HttpServletRequest方法以提供模拟对象。

myServlet.doGet(new HttpServletRequestWrapper() {
     public HttpSession getSession() {
         return mockSession;
     }

     ...
}

HttpServletRequestWrapper是一个便捷的Java类。我建议我们在单元测试中创建一个实用程序方法来创建模拟http请求:

public void testSomething() {
    myServlet.doGet(createMockRequest(), createMockResponse());
}

protected HttpServletRequest createMockRequest() {
   HttpServletRequest request = new HttpServletRequestWrapper() {
        //overrided methods   
   }
}

最好将模拟创建方法放在基础servlet超类中,并进行所有servlet单元测试以对其进行扩展。

大多数时候,我都是通过"集成测试"而不是纯粹的单元测试来测试Servlet和JSP。有大量的JUnit / TestNG插件,包括:

  • HttpUnit(最古老和最著名的,非常低的级别,根据需要可能是好是坏)
  • HtmlUnit(比HttpUnit更高的级别,对于许多项目来说更好)
  • JWebUnit(位于其他测试工具之上,并试图简化它们-我更喜欢这种工具)
  • WatiJ和Selenium(使用浏览器进行测试,虽然更重,但更现实)

这是针对简单的Order Processing Servlet的JWebUnit测试,该Servlet处理来自" orderEntry.html"形式的输入。它需要一个客户ID,一个客户名称和一个或者多个订单项:

public class OrdersPageTest {
    private static final String WEBSITE_URL = "http://localhost:8080/demo1";

    @Before
    public void start() {
        webTester = new WebTester();
        webTester.setTestingEngineKey(TestingEngineRegistry.TESTING_ENGINE_HTMLUNIT);
        webTester.getTestContext().setBaseUrl(WEBSITE_URL);
    }
    @Test
    public void sanity() throws Exception {
        webTester.beginAt("/orderEntry.html");
        webTester.assertTitleEquals("Order Entry Form");
    }
    @Test
    public void idIsRequired() throws Exception {
        webTester.beginAt("/orderEntry.html");
        webTester.submit();
        webTester.assertTextPresent("ID Missing!");
    }
    @Test
    public void nameIsRequired() throws Exception {
        webTester.beginAt("/orderEntry.html");
        webTester.setTextField("id","AB12");
        webTester.submit();
        webTester.assertTextPresent("Name Missing!");
    }
    @Test
    public void validOrderSucceeds() throws Exception {
        webTester.beginAt("/orderEntry.html");
        webTester.setTextField("id","AB12");
        webTester.setTextField("name","Joe Bloggs");

        //fill in order line one
        webTester.setTextField("lineOneItemNumber", "AA");
        webTester.setTextField("lineOneQuantity", "12");
        webTester.setTextField("lineOneUnitPrice", "3.4");

        //fill in order line two
        webTester.setTextField("lineTwoItemNumber", "BB");
        webTester.setTextField("lineTwoQuantity", "14");
        webTester.setTextField("lineTwoUnitPrice", "5.6");

        webTester.submit();
        webTester.assertTextPresent("Total: 119.20");
    }
    private WebTester webTester;
}

Mockrunner(http://mockrunner.sourceforge.net/index.html)可以做到这一点。它提供了一个模拟的J2EE容器,可用于测试Servlet。它还可以用于对其他服务器端代码(如EJB,JDBC,JMS,Struts)进行单元测试。我自己只使用了JDBC和EJB功能。