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。
什么是集成单元测试
在容器内进行单元测试,可谓是一举两得。集成单元测试介于逻辑单元测试和功能单元测试之间。下图说明了关系:
看不到图点这里>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
从另外的一个角度看,集成测试就是逻辑单元测试和功能单元测试的一般化。如果你把测试的开始位置设在某个方法中,将测试的结束位置设在同一个方法中,那么结果就是逻辑单元测试。如果你将开始的位置和结束的位置放在应用程序的某个入口,那么结果就是功能测试。
主要的含义就是,没什么东西是全黑或是全白的,有时候,你要管理应用程序需要更多的灵活性,有时候,你的测试介于二者之间。
在下面介绍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的工作原理有一定的了解!
第一步,执行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不能解决的问题了!
- JUnit学习笔记11---用Cactus进行容器内测试
- JUnit学习笔记11---用Cactus进行容器内测试
- 使用 JUnit 进行容器内测试
- jetty+cactus完成容器内测试
- JUnit学习笔记6---用stub进行粗粒度测试
- JUnit学习笔记6---用stub进行粗粒度测试
- JUnit学习笔记6---用stub进行粗粒度测试
- Cactus容器测试入门
- 使用 JUnit 进行容器内测试(常用的两种测试方法)
- Cactus容器测试 ---浏览器方式
- junit 测试学习笔记
- JUnit学习笔记7---mock objects进行孤立测试1
- JUnit学习笔记8---mock object进行独立测试2
- JUnit学习笔记9---mock object进行孤立测试3
- JUnit学习笔记10---mock object进行孤立测试4
- [Android学习笔记]使用Android Junit进行测试的注意事项
- JUnit学习笔记10---mock object进行孤立测试4
- JUnit学习笔记9---mock object进行孤立测试3
- 虚拟机Linux与宿主机Windows XP文件互访
- 迷宫课设 绝对原创 C版
- boost {asio reactor}
- LoadRunner测试GWT
- windows xp组件中没有IIS选项的解决方案
- JUnit学习笔记11---用Cactus进行容器内测试
- J2EE下使用AJAX(五) jsonplugin -- struts2下的AJAX插件
- 用VB.Net读取Resources.resx文件
- 魔兽世界 MPQ(MoPaQ) 文件相关资料
- 一个还原备份文件的问题
- UI Automation-MultipleViewPattern
- WIN7下配置IIS+ASP
- 堆排序算法时间复杂度推导
- Java中Graphics用法