JUnit学习笔记13---对servlet和filter进行单元测试2

来源:互联网 发布:淘宝外贸原单可以买吗 编辑:程序博客网 时间:2024/06/09 23:25

 用mock objects测试servlet

我们已经看过了用Cactus进行servlet的测试,接下来我们使用mock objects来做同样的练习。

在前面mock的学习中,我们使用了EasyMock编写mock objects。这次我们使用的是DynaMock API 来编写,DynaMock API是MockObjects.com中的一部分,他们都是用DynaMock Proxies在运行时间内生成mock objects。然而,在结构上,DynaMock比EasyMock更加的具有优势:它的API更加的全面(特别是在定义预期的方法里面),而且它生成的代码较简洁。缺点是它的使用稍微的复杂一点,并且是个不太成熟的框架。

EasyMock与DynaMock的比较

DynaMock更加简洁的代码(约是EasyMock的一半)EasyMock提供更加健壮的类型转换,对自动完成和接口改变时非常有用DynaMock有一个更加全面的APIEasyMock更加成熟

 使用DynaMocks和DynaBeans写一个测试

下面的代码重新实现了上一笔记中描述的testGetCommandOK和testGetCommandNotDefined

package junitbook.servlets;import java.util.ArrayList;import java.util.Collection;import java.util.List;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import junit.framework.TestCase;import org.apache.commons.beanutils.BasicDynaClass;import org.apache.commons.beanutils.DynaBean;import org.apache.commons.beanutils.DynaProperty;import com.mockobjects.dynamic.C;import com.mockobjects.dynamic.Mock;public class TestAdminServletMO extends TestCase{    private Mock mockRequest;                 建立新的mock    private Mock mockResponse;    private HttpServletRequest request;    private HttpServletResponse response;    private AdminServlet servlet;        protected void setUp()    {        servlet = new AdminServlet()                            ;        {                                                       ;            public Collection executeCommand(String command)    ;                throws Exception                                ;            {                                                        return createCommandResult();            }        };        mockRequest = new Mock(HttpServletRequest.class);  你在代码中使用了一个HttpServletRequest对象来测试;之所以这样做是因为          request = (HttpServletRequest) mockRequest.proxy();你不是在一个容器内运行,你需要为它创建一个仿制品。        mockResponse = new Mock(HttpServletResponse.class);     设定mock的HttpServletRequest对象的行为,同时让DynaMock去查证        response = (HttpServletResponse) mockResponse.proxy();  方法被调用而且传递的参数与所期望的匹配。    }    protected void tearDown()  ;让你的mock查证你设置的预期和你为之定义的行为的方法被调用了。    {        mockRequest.verify();    }            public void testGetCommandOk() throws Exception    {        mockRequest.expectAndReturn("getParameter", "command",       ;当你带有”command”字符串作为参数的getParameter方法时,            "SELECT...");                                            ;让mock返回“select…”                String command = servlet.getCommand(request);                assertEquals("SELECT...", command);    }      public void testGetCommandNotDefined()    {        mockRequest.expectAndReturn("getParameter",             ;当调用了带有字符串getParameter方法时,让mock返回null            C.isA(String.class), null);                try         {            servlet.getCommand(request);            fail("Command should not have existed");        }        catch (ServletException expected)        {            assertTrue(true);        }            }    private Collection createCommandResult() throws Exception    {        List results = new ArrayList();                DynaProperty[] props = new DynaProperty[] {            new DynaProperty("id", String.class),            new DynaProperty("responsetime", Long.class)        };        BasicDynaClass dynaClass = new BasicDynaClass("requesttime",            null, props);        DynaBean request1 = dynaClass.newInstance();        request1.set("id", "12345");        request1.set("responsetime", new Long(500));        results.add(request1);        DynaBean request2 = dynaClass.newInstance();        request1.set("id", "56789");        request1.set("responsetime", new Long(430));        results.add(request2);                return results;    }             public void testCallView() throws Exception    {        servlet.callView(request);    }    public void testDoGet() throws Exception    {        mockRequest.expectAndReturn("getParameter", "command",             "SELECT...");        // Verify that the result of executing the command has been        // stored in the HTTP request as an attribute that will be        // passed to the JSP page.        mockRequest.expect("setAttribute", C.args(C.eq("result"),             C.isA(Collection.class)));        servlet.doGet(request, response);    }}

用Cactus写filter测试

对SecurityFilter的要求是拦截所有的Http请求并且查证传入的SQL语句不包含任何的恶意指令。目前,你只要检查SQL查询中是否包含SELECT语句;如果不包含,将会转到一个错误页面。

SecurityFilter.java

package junitbook.servlets;import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.RequestDispatcher;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;public class SecurityFilter implements Filter{    private String securityErrorPage;        public void init(FilterConfig theConfig) throws ServletException    {        this.securityErrorPage =             theConfig.getInitParameter("securityErrorPage");   从web.xml获得错误页面的名字    }    public void doFilter(ServletRequest theRequest,         ServletResponse theResponse, FilterChain theChain)        throws IOException, ServletException    {        String sqlCommand =             theRequest.getParameter(AdminServlet.COMMAND_PARAM);                if (!sqlCommand.startsWith("SELECT"))        {            // Forward to an error page            RequestDispatcher dispatcher =                 theRequest.getRequestDispatcher(         转向错误页面                this.securityErrorPage);            dispatcher.forward(theRequest, theResponse);                    }        else        {            theChain.doFilter(theRequest, theResponse);                    }    }    public void destroy()    {    }}

用Cactus测试这个filter与你已经测试过的AdminServlet非常的相似。主要的差别在于TestCase继承是FilterTestCase而不是ServletTestCase。这种变化允许测试访问Filter API对象(FilterConfig,Request,Response和FilterChain)。

用SELECT查询测试Filter

测试的是当传来的SQL查询是SELECT查询时的doFilter方法。

import java.io.IOException;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import org.apache.cactus.FilterTestCase;import org.apache.cactus.WebRequest;import org.apache.cactus.WebResponse;public class TestSecurityFilter extends FilterTestCase{    public void beginDoFilterAllowedSQL(WebRequest request)    {        request.addParameter("command", "SELECT [...]");    }        public void testDoFilterAllowedSQL() throws Exception    {        SecurityFilter filter = new SecurityFilter();        FilterChain mockFilterChain = new FilterChain()        {            public void doFilter(ServletRequest theRequest,                 ServletResponse theResponse) throws IOException,                 ServletException            {            }            public void init(FilterConfig theConfig)            {            }            public void destroy()            {            }        };        filter.doFilter(request, response, mockFilterChain);            }    public void beginDoFilterForbiddenSQL(WebRequest request)    {        request.addParameter("command", "UPDATE [...]");    }    public void testDoFilterForbiddenSQL() throws Exception    {        config.setInitParameter("securityErrorPage",             "/securityError.jsp");        SecurityFilter filter = new SecurityFilter();        filter.init(config);        filter.doFilter(request, response, filterChain);            }    public void endDoFilterForbiddenSQL(WebResponse response)    {        assertTrue("Bad response page",             response.getText().indexOf(                "<title>Security Error Page</title>") > 0);            }}
 对于这个测试,使用Cactus beginXXX方法把SQL指令加入你的filter处理的HTTP请求中,注意你的SQL查询是一个SELECT查询。让filter不要调
用链中的下一个filter(或jsp/servlet target),这样你就创建了一个FilterChain的空实现。你也可以让filter调用链中的一个元素,然而
一个filter与处理链中在该filter之后调用的filter/jsp/servlet target是完全独立的,这就是得单独测试filter更有意义,特别是考虑到
filter不会更改返回的http响应。 

何时使用Cactus,何时使用mock objects

  • 主要差异在于Cactus不仅能运行单元测试,而且能运行集成测试和某种程度上的功能测试,当然,更多的好处带来更多的复杂性。
  • mock比较难写
  • Cactus为那些你只需要设置的一些初始条件的情况提供实体对象
  • 如果是对单元测试的应用程序已经写了,它通常必须是重复代理过的以支持mock objects测试。额外的重复代理对Cactus通常不需要。

一个好的策略就是要将商务逻辑代码从集成代码(与容器相互作用的代码)分离。然后

  • 使用mock 测试商务代码
  • 使用Cactus测试集成代码

小结:本章示范了如何对servlet和filter进行单元测试,在更广泛的意义上是对使用了Servlet/filter API的任何代码进行单元测试。

            在下一章,我们将把注意力转换到JSP和Taglib API上。 

原创粉丝点击