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

来源:互联网 发布:手机nfc读身份证软件 编辑:程序博客网 时间:2024/05/16 02:00

      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不能解决的问题了!
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 婴儿20天不大便怎么办 新生儿8天没大便怎么办 新生儿2天没大便怎么办 新生儿4天没大便怎么办 2岁宝宝便秘严重怎么办 婴儿7天没有大便怎么办 新生儿6天没大便怎么办 大便带鲜血 不疼怎么办 宝宝发烧到39度怎么办 肛裂大便出鲜血怎么办 拉大便有血怎么办啊 长痔疮拉大便血怎么办 病人卧床太久不解大便怎么办 宝宝拉泡沫便便怎么办 孩子拉肚子怎么办呢有好办法吗 2岁宝宝大便带血怎么办 十个月宝宝肛裂怎么办 大便时拉出血怎么办啊 2岁宝宝大便干燥怎么办 5岁儿童大便干燥怎么办 九个月宝宝平血怎么办 6个月宝宝肛裂怎么办 3岁儿童大便干燥怎么办 外痔疮出血了该怎么办 外痔疮流血了怎么办呢 外痔疮破了出血怎么办 拉稀拉的肛门疼怎么办 大人屁股沟裂了怎么办 肛门痛大便有血怎么办 肛裂出血几天了怎么办 肛裂拉屎出血该怎么办 孕期肛裂出血该怎么办 老人大便拉不出来怎么办 拉屎拉的屁眼疼怎么办 拉屎堵在肛门口怎么办 上火拉大便有血怎么办 7岁儿童大便带血怎么办 阴炎用药后出血怎么办 孕晚期大便拉不出来怎么办 想拉屎拉不出来怎么办 4岁幼儿大便干燥怎么办