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

 用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有一个更加全面的API
EasyMock更加成熟

 使用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上。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值