JUnit学习笔记11---用Cactus进行容器内测试

In-container testing with Cactus                        Good design at good times.Make it run,make it run right.----Kent Beck

本章内容:

  • 对组件进行单元测试时使用mock objects的缺点
  • 介绍使用Cactus进行容器内测试
  • Cactus的工作原理

      对组件进行单元测试的问题

    想象一下,有个Web应用程序,它使用了servlet,你希望对SampleServlet servlet的isAuthenticated方法进行单元测试。

package junitbook.container;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class SampleServlet extends HttpServlet
{
    public boolean isAuthenticated(HttpServletRequest request)
    {
        HttpSession session = request.getSession(false);

        if (session == null)
        {
            return false;
        }
        
        String authenticationAttribute = 
            (String) session.getAttribute("authenticated");
            
        return Boolean.valueOf(
            authenticationAttribute).booleanValue();
    }
}

         为了能够测试这个方法,你需要得到一个合法的HttpServletRequest对象。不幸的是,不可能调用new HttpServletRequest来创建一个可用的请求。HttpServletRequest的生命周期是由容器管理的。无法单独的使用JUnit为isAuthenticated方法编写测试。

  定义   组件/容器  ---组件是在容器内执行的一段代码。容器则是在位存放在内的组件提供有用服务(比如生命周期、安全、事物、分布等等)的器皿。

        幸运的是我们可以有几种解决方案,概括的说有两种核心的解决方案:用mock objects进行容器外测试和用Cactus进行容器内测试。

       这两种方法的一个变体就是stub。mock和Cactus都是可行的,接下来将讨论两种实现,以及两者的比较。

    用mock objects测试组件

    我们将试着用mock来测试servlet,然后讨论这种方式的优劣。有几个框架可以生成mock objects,本章中我们将使用EasyMock。

    EasyMock是现在最著名的mock objects生成器之一。EasyMock是用java的动态代理(dynamic proxies)实现的,所以可以透明的运行时生成mock objects。

      用EasyMock来测试servlet例子

       我们想到的对isAuthenticated方法进行单元测试的方法是用第七章所描述的mock objects方法来模拟HttpServletRequest类。这种方式是可以工作的,但是你要编不少的代码。如果是使用EasyMock框架,那么我们可以轻松的做到这一点!

package junitbook.container;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.easymock.MockControl;

import junit.framework.TestCase;

public class TestSampleServlet extends TestCase
{
    private SampleServlet servlet;
    
    private MockControl controlHttpServlet;                 ;创建两个mock objects,一个是HttpServletRequest,另一个是HttpSess
    private HttpServletRequest mockHttpServletRequest;      ;ion。EasyMock通过为mock创建动态代理来工作,动态代理是通过MockContr
                                                            ;ol控制对象来控制的。
    private MockControl controlHttpSession;                 ;
    private HttpSession mockHttpSession;                    ;
    
    protected void setUp()
    {
        servlet = new SampleServlet();
        
        controlHttpServlet = MockControl.createControl(
            HttpServletRequest.class);
        mockHttpServletRequest = 
            (HttpServletRequest) controlHttpServlet.getMock();
        
        controlHttpSession = MockControl.createControl(
            HttpSession.class);
        mockHttpSession = 
            (HttpSession) controlHttpSession.getMock();
    }

    protected void tearDown()
    {
        controlHttpServlet.verify(); 让EasyMock来校验确实是调用了被模拟的方法,如果你定义了一个mock,但是却没有被调用,那么将会
        controlHttpSession.verify(); 报错。如果你没有为一个方法定义任何的行为,但是它被调用了,那么也会报错。这是在tearDown方法中   
    }                              ;做的。这样就可以对所有的测试自动校验。
    
    public void testIsAuthenticatedAuthenticated()
    {
        mockHttpServletRequest.getSession(false);           ;使用控制对象告诉HttpServletRequest mock object,当getSession
        controlHttpServlet.setReturnValue(mockHttpSession);  (false)方法被调用时,这个方法应当返回一个HttpSession mock object

        mockHttpSession.getAttribute("authenticated");      ;这里和上面一样,定义了方法被调用后的返回情况,这种是EasyMock的训练模
        controlHttpSession.setReturnValue("true");          ;式(training mode)
        
        controlHttpServlet.replay();       一旦你激活了这的mock object,你就走出了训练模式,调用mock objects会返回预期的结果。
        controlHttpSession.replay();
                
        assertTrue(servlet.isAuthenticated(mockHttpServletRequest));
    }

    public void testIsAuthenticatedNotAuthenticated()               ;检测servlet中没有authenticated时
    {
        mockHttpServletRequest.getSession(false);
        controlHttpServlet.setReturnValue(mockHttpSession);

        mockHttpSession.getAttribute("authenticated");
        controlHttpSession.setReturnValue(null);
        
        controlHttpServlet.replay();       
        controlHttpSession.replay();
                
        assertFalse(servlet.isAuthenticated(mockHttpServletRequest));
    }

    public void testIsAuthenticatedNoSession()                      ;检测没有Session时
    {
        mockHttpServletRequest.getSession(false);
        controlHttpServlet.setReturnValue(null);
       
        controlHttpServlet.replay();       
        controlHttpSession.replay();
                
        assertFalse(servlet.isAuthenticated(mockHttpServletRequest));
    }

}
    用了上面的方法,我们在回忆一下在前面笔记中记录的情况,这正是个玩笑,因为,前面没有使用EasyMock时,我们运用很多模式去重构代码,紧在这
里看来是不必要的,而重构代码有各种好。
     接下来分析一下使用mock objects的优势和不足,最大的优点就是,执行测试时可以不需要运行容器。很快就能建立起测试,而且运行的速度也很快。
主要的缺点是:组件并不是在部署他们的容器中进行测试的,你无法测试组件和容器的交互,而这也是代码的重要部分。你也没有测试运行于容器内部的不同
组件之间的交互。运用mock还有其他的缺点,比如大量的mock将带来大量的开销,而且在测试时一定要知道被模拟组件的API,很多API是不那么好理解的。
幸运的是,有一种技术来弥补这些不足---Cactus。
什么是集成单元测试
   在容器内进行单元测试,可谓是一举两得。集成单元测试介于逻辑单元测试和功能单元测试之间。下图说明了关系:
    看不到图点这里>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    image 

从另外的一个角度看,集成测试就是逻辑单元测试和功能单元测试的一般化。如果你把测试的开始位置设在某个方法中,将测试的结束位置设在同一个方法中,那么结果就是逻辑单元测试。如果你将开始的位置和结束的位置放在应用程序的某个入口,那么结果就是功能测试。

主要的含义就是,没什么东西是全黑或是全白的,有时候,你要管理应用程序需要更多的灵活性,有时候,你的测试介于二者之间。

   在下面介绍Jakarta Cactus,一个专门用来对服务器端java组件进行集成单元测试的框架。

  介绍Cactus

  Cactus是针对集成单元测试的,Cactus的远景基本是这样的:在过去今年里,java代码转向元数据,对组件的编写者,提供的服务越来越透明,最近的趋势是使用AOP编程,实现编程同业务代码解耦。基本业务代码在减少,但是元数据配置在增多,集成测试变得越来越重要,Cactus对集成测试是拥抱的态度,将之融入开发周期,并且提供细粒度的解决方案。

  用Cactus测试组件

测试代码如下:

package junitbook.container;

import org.apache.cactus.ServletTestCase;
import org.apache.cactus.WebRequest;

public class TestSampleServletIntegration extends ServletTestCase
{
    private SampleServlet servlet;    

    protected void setUp()
    {
        servlet = new SampleServlet();
    }
    
    public void testIsAuthenticatedAuthenticated()
    {
        session.setAttribute("authenticated", "true");
        
        assertTrue(servlet.isAuthenticated(request));
    }

    public void testIsAuthenticatedNotAuthenticated()
    {
        assertFalse(servlet.isAuthenticated(request));
    }

    public void beginIsAuthenticatedNoSession(WebRequest request)
    {
        request.setAutomaticSession(false);
    }
    
    public void testIsAuthenticatedNoSession()
    {
        assertFalse(servlet.isAuthenticated(request));
    }
}

Cactus框架将容器对象(这里是HttpServletRequest和HttpSession)提供给测试。这样就使其变得快捷和简单!

  运行Cactus

前面我们提到了Jetty,那么使用Jetty+Cactus是非常不错的选择,他们的好处是可以运用在任何的IDE上!

   为了通过Jetty运行Cactus测试,你需要创建一个JUnit test suite,它要包含测试的内容,而且还要用Cactus提供的JettyTestSetup类包装起来。

package junitbook.container;

import org.apache.cactus.extension.jetty.JettyTestSetup;

import junit.framework.Test;
import junit.framework.TestSuite;

public class TestAllWithJetty 
{
    public static Test suite()
    {
        System.setProperty("cactus.contextURL",
            "http://localhost:8080/test");

        TestSuite suite = new TestSuite("All tests with Jetty");
        suite.addTestSuite(TestSampleServletIntegration.class);
        return new JettyTestSetup(suite);
    }
}

为了使程序能够运行,我们还不得不做一些工作,将Cactus jar、Commons Logging jar、Commons HttpClient jar、AspectJ runtime jar和Jetty jar加入到我们的工作jar库中!

容器内测试的缺点:

  • 需要特定的工具
  • 更长的执行时间
  • 复杂的配置

   Cactus如何工作

    下面的章节将是本书最终要达成的目标,也是为了完成目标,我们必须对Cactus的工作原理有一定的了解!

    182755_2009110118415114syB                          

   第一步,执行beginXXX

                如果存在beginXXX方法,那么Cactus就会执行这个方法。这个beginXXX方法让你把信息传递给redirector。TestSampleServletIntegration

例子继承了ServletTestCase并且连接到Cactus servlet redirector。servlet redirector被实现为一个servlet,这是容器的入口。Cactus客户端通过打开HTTP连接来调用servlet redirector。beginXXX方法会建立Http相关的参数,这些参数在servlet redirector所收到的http请求中设置。这个方法可以用来定义http post/get参数、http cookie以及http header等等。例如:

              public void beginXXX( WebRequest request)

                {request.addParameter(“param1”,”value1”);

                 request.addCookie(“cookie1”,”value1”);

                   }

                     在TestSampleServletIntegration类中,我们用beginXXX方法来告诉redirector方法不要创建HTTP session

         public void beginIsAuthenicatedNoSession(WebRequest request){
          request.setAutomaticSession(false);
          }
 第二步,打开redirector连接
        YYYTestCase打开到redirector的连接。在这个例子中,ServletTestCase代码打开到servlet redirector的http连接。
 第三步,创建服务器端的TestCase实例
        Redirector会创建YYYTestCase的一个实例,注意这是Cactus创建的第二个实例,第一个是在客户端创建的。之后redirector类获得了对象的实
例,并且通过设置类变量把他们赋给YYYTestCase实例。
  第四步,在服务器端调用setUp、testXXX、tearDown
  第五步,执行endXXX
  最后一步,搜集测试结果
小结: 要对容器应用程序进行单元测试,采用纯JUnit就显得力不从心了,我们采用
mock objects可以应付自如,但是,我们这样做就缺少了一些测试,而今,我们有了
Cactus就可以解决mock不能解决的问题了!
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值