- 浏览: 261492 次
- 性别:
- 来自: 成都
文章分类
最新评论
-
zhagener:
package com.huawei.qyq.impl;imp ...
EasyMock使用说明 -
LetCode:
将String转换成InputStream -
Mr_kimilo:
MyEclipse6.5安装的时候出现问题: JS Test ...
javascript测试工具: JsTestDriver -
jersey109:
我同意楼下的,SQLException和IOException ...
check exception和uncheck exception -
jersey109:
楼主,你不说CODE,我觉得你对RuntimeExceptio ...
check exception和uncheck exception
单元测试是XP极力推荐的测试驱动开发模式,是保证软件质量的重要方法。尽管如此,对许多类的单元测试仍然是极其困难的,例如,对数据库操作的类进行测试,如果不准备好数据库环境以及相关测试数据,是很难进行单元测试的;再例如,对需要运行在容器内的Servlet或EJB组件,脱离了容器也难于测试。
幸运的是,Mock Object可以用来模拟一些我们需要的类,这些对象被称之为模仿对象,在单元测试中它们特别有价值。
Mock Object用于模仿真实对象的方法调用,从而使得测试不需要真正的依赖对象。Mock Object只为某个特定的测试用例的场景提供刚好满足需要的最少功能。它们还可以模仿错误的条件,例如抛出指定的异常等。
目前,有许多可用的Mock类库可供我们选择。一些Mock库提供了常见的模仿对象,例如:HttpServletRequest,而另一些Mock库则提供了动态生成模仿对象的功能,本文将讨论使用EasyMock动态生成模仿对象以便应用于单元测试。
到目前为止,EasyMock提供了1.2版本和2.0版本,2.0版本仅支持Java SE 5.0,本例中,我们选择EasyMock 1.2 for Java 1.3版本进行测试,可以从http://www.easymock.org下载合适的版本。
我们首先来看一个用户验证的LoginServlet类:
/**
* LoginServlet.java
* Author: Liao Xue Feng, www.javaeedev.com
*/
package com.javaeedev.test.mock;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
// check username & password:
if("admin".equals(username) && "123456".equals(password)) {
ServletContext context = getServletContext();
RequestDispatcher dispatcher = context.getNamedDispatcher("dispatcher");
dispatcher.forward(request, response);
}
else {
throw new RuntimeException("Login failed.");
}
}
}
这个Servlet实现简单的用户验证的功能,若用户名和口令匹配“admin”和“123456”,则请求被转发到指定的dispatcher上,否则,直接抛出RuntimeException。
为了测试doPost()方法,我们需要模拟HttpServletRequest,ServletContext和RequestDispatcher对象,以便脱离J2EE容器来测试这个Servlet。
我们建立TestCase,名为LoginServletTest:
public class LoginServletTest extends TestCase {
}
我们首先测试当用户名和口令验证失败的情形,演示如何使用EasyMock来模拟HttpServletRequest对象:
public void testLoginFailed() throws Exception {
MockControl mc = MockControl.createControl(HttpServletRequest.class);
HttpServletRequest request = (HttpServletRequest)mc.getMock();
// set Mock Object behavior:
request.getParameter("username");
mc.setReturnValue("admin", 1);
request.getParameter("password");
mc.setReturnValue("1234", 1);
// ok, all behaviors are set!
mc.replay();
// now start test:
LoginServlet servlet = new LoginServlet();
try {
servlet.doPost(request, null);
fail("Not caught exception!");
}
catch(RuntimeException re) {
assertEquals("Login failed.", re.getMessage());
}
// verify:
mc.verify();
}
仔细观察测试代码,使用EasyMock来创建一个Mock对象需要首先创建一个MockControl:
MockControl mc = MockControl.createControl(HttpServletRequest.class);
然后,即可获得MockControl创建的Mock对象:
HttpServletRequest request = (HttpServletRequest)mc.getMock();
下一步,我们需要“录制”Mock对象的预期行为。在LoginServlet中,先后调用了request.getParameter("username")和request.getParameter("password")两个方法,因此,需要在MockControl中设置这两次调用后的指定返回值。我们期望返回的值为“admin”和“1234”:
request.getParameter("username"); // 期望下面的测试将调用此方法,参数为"username"
mc.setReturnValue("admin", 1); // 期望返回值为"admin",仅调用1次
request.getParameter("password"); // 期望下面的测试将调用此方法,参数为" password"
mc.setReturnValue("1234", 1); // 期望返回值为"1234",仅调用1次
紧接着,调用mc.replay(),表示Mock对象“录制”完毕,可以开始按照我们设定的方式运行,我们对LoginServlet进行测试,并预期会产生一个RuntimeException:
LoginServlet servlet = new LoginServlet();
try {
servlet.doPost(request, null);
fail("Not caught exception!");
}
catch(RuntimeException re) {
assertEquals("Login failed.", re.getMessage());
}
由于本次测试的目的是检查当用户名和口令验证失败后,LoginServlet是否会抛出RuntimeException,因此,response对象对测试没有影响,我们不需要模拟它,仅仅传入null即可。
最后,调用mc.verify()检查Mock对象是否按照预期的方法调用正常运行了。
运行JUnit,测试通过!表示我们的Mock对象正确工作了!
下一步,我们来测试当用户名和口令匹配时,LoginServlet应当把请求转发给指定的RequestDispatcher。在这个测试用例中,我们除了需要HttpServletRequest Mock对象外,还需要模拟ServletContext和RequestDispatcher对象:
MockControl requestCtrl = MockControl.createControl(HttpServletRequest.class);
HttpServletRequest requestObj = (HttpServletRequest)requestCtrl.getMock();
MockControl contextCtrl = MockControl.createControl(ServletContext.class);
final ServletContext contextObj = (ServletContext)contextCtrl.getMock();
MockControl dispatcherCtrl = MockControl.createControl(RequestDispatcher.class);
RequestDispatcher dispatcherObj = (RequestDispatcher)dispatcherCtrl.getMock();
按照doPost()的语句顺序,我们设定Mock对象指定的行为:
requestObj.getParameter("username");
requestCtrl.setReturnValue("admin", 1);
requestObj.getParameter("password");
requestCtrl.setReturnValue("123456", 1);
contextObj.getNamedDispatcher("dispatcher");
contextCtrl.setReturnValue(dispatcherObj, 1);
dispatcherObj.forward(requestObj, null);
dispatcherCtrl.setVoidCallable(1);
requestCtrl.replay();
contextCtrl.replay();
dispatcherCtrl.replay();
然后,测试doPost()方法,这里,为了让getServletContext()方法返回我们创建的ServletContext Mock对象,我们定义一个匿名类并覆写getServletContext()方法:
LoginServlet servlet = new LoginServlet() {
public ServletContext getServletContext() {
return contextObj;
}
};
servlet.doPost(requestObj, null);
最后,检查所有Mock对象的状态:
requestCtrl.verify();
contextCtrl.verify();
dispatcherCtrl.verify();
运行JUnit,测试通过!
倘若LoginServlet的代码有误,例如,将context.getNamedDispatcher("dispatcher")误写为 context.getNamedDispatcher("dispatcher2"),则测试失败,JUnit报告:
junit.framework.AssertionFailedError:
Unexpected method call getNamedDispatcher("dispatcher2"):
getNamedDispatcher("dispatcher2"): expected: 0, actual: 1
getNamedDispatcher("dispatcher"): expected: 1, actual: 0
at ...
完整的LoginServletTest代码如下:
/** * LoginServletTest.java * Author: Liao Xue Feng, www.javaeedev.com */ package com.javaeedev.test.mock;
import javax.servlet.*;
import javax.servlet.http.*;
import org.easymock.*; import junit.framework.TestCase;
public class LoginServletTest extends TestCase {
public void testLoginFailed() throws Exception {
MockControl mc = MockControl.createControl(HttpServletRequest.class);
HttpServletRequest request = (HttpServletRequest)mc.getMock();
// set Mock Object behavior:
request.getParameter("username");
mc.setReturnValue("admin", 1);
request.getParameter("password");
mc.setReturnValue("1234", 1);
// ok, all behaviors are set!
mc.replay();
// now start test:
LoginServlet servlet = new LoginServlet();
try {
servlet.doPost(request, null);
fail("Not caught exception!");
}
catch(RuntimeException re) {
assertEquals("Login failed.", re.getMessage());
}
// verify:
mc.verify();
}
public void testLoginOK() throws Exception { // create mock: MockControl requestCtrl = MockControl.createControl(HttpServletRequest.class); HttpServletRequest requestObj = (HttpServletRequest)requestCtrl.getMock(); MockControl contextCtrl = MockControl.createControl(ServletContext.class); final ServletContext contextObj = (ServletContext)contextCtrl.getMock(); MockControl dispatcherCtrl = MockControl.createControl(RequestDispatcher.class); RequestDispatcher dispatcherObj = (RequestDispatcher)dispatcherCtrl.getMock(); // set behavior: requestObj.getParameter("username"); requestCtrl.setReturnValue("admin", 1); requestObj.getParameter("password"); requestCtrl.setReturnValue("123456", 1); contextObj.getNamedDispatcher("dispatcher"); contextCtrl.setReturnValue(dispatcherObj, 1); dispatcherObj.forward(requestObj, null); dispatcherCtrl.setVoidCallable(1); // done! requestCtrl.replay(); contextCtrl.replay(); dispatcherCtrl.replay(); // test: LoginServlet servlet = new LoginServlet() { public ServletContext getServletContext() { return contextObj; } }; servlet.doPost(requestObj, null); // verify: requestCtrl.verify(); contextCtrl.verify(); dispatcherCtrl.verify(); } }
总结:
虽然EasyMock可以用来模仿依赖对象,但是,它只能动态模仿接口,无法模仿具体类。这一限制正好要求我们遵循“针对接口编程”的原则:如果不针对接口,则测试难于进行。应当把单元测试看作是运行时代码的最好运用,如果代码在单元测试中难于应用,则它在真实环境中也将难于应用。总之,创建尽可能容易测试的代码就是创建高质量的代码。
发表评论
-
开源性能测试工具 - Apache ab 介绍
2009-12-15 14:08 2183版权声明:本文可以被转载,但是在 ... -
mock UrlPreparerTest
2009-12-15 14:02 1046View Javadoc 1 /* 2 * $I ... -
servlet 单元测试 mock easymock2 junit4
2009-12-15 14:00 2707Java代码 package net ... -
EasyMock 使用
2009-12-15 13:59 1457背景: EasyMock 2 版本必 ... -
EasyMock使用说明
2009-12-15 13:54 2945开发环境版本 JDK 1.5 ... -
使用 EasyMock 更轻松地进行测试
2009-12-15 13:50 1193测试驱动开发是软件开 ... -
EasyMock 2.0_ReleaseCandidate 文档翻译
2009-12-15 13:49 1548EasyMock 2.0_ReleaseCandidate 文 ... -
servlet 单元测试
2009-12-01 10:10 2135写了个Servlet的测试用例,初学单元测试,大家帮我看看。 ... -
Web下的整体测试
2009-10-15 15:33 775随着Internet的日益普及 ... -
Selenium Core
2009-10-15 15:28 2363先来介绍下Selenium Core: Selenium C ... -
Selenium 使用介绍
2009-10-15 15:04 4791Selenium 严格说来,Selenium是一套完整的We ...
相关推荐
本文将对 EasyMock 的功能和原理进行介绍,并通过示例来说明如何使用 EasyMock 进行单元测试。 Mock 方法是单元测试中常见的一种技术,它的主要作用是模拟一些在应用中不容易构造或者比较复杂的对象,从而把测试与...
EasyMock 是一套通过简单的方法对于指定的接口或类生成 Mock 对象的类库,它能利用对接口或类的模拟来辅助...本文将向您展示如何使用 EasyMock 进行单元测试,并对 EasyMock 的原理进行分析。 标签:easyMock
通过 EasyMock,我们可以方便的构造 Mock 对象从而使单元测试顺利进行。 安装 在 maven 项目中除了要引入 junit 的 jar 包之外,在 pom.xml 文件中添加 easymock 的依赖库定义,例如: ``` <groupId>org....
Mock 方法是单元测试中常见的一种技术,它的主要作用是模拟一些在应用中不容易构造或者比较复杂的对象,从而把测试与测试边界以外的对象隔离开。 编 写自定义的 Mock 对象需要额外的编码工作,同时也可能引入错误。...
在实际应用中,EasyMock常常与诸如JUnit这样的测试框架结合使用,可以大大提高单元测试的效率和质量。同时,EasyMock还提供了扩展功能,如EasyMock Class Extension,可以帮助自动管理模拟对象的生命周期,简化测试...
### 使用Easymock进行单元测试 在单元测试中,通常遵循以下步骤使用Easymock: 1. **创建模拟对象**:首先,需要创建你要测试的目标类所依赖的对象的模拟版本。Easymock提供了`EasyMock.createMock()`方法来创建...
轻松模拟 EasyMock是一个Java库,它提供了一种在单元测试中使用Mock对象的简便方法。 您可以在找到网站和用户文档。开发者信息建置状态环境设定我正在使用: IntelliJ 2020.3 Ultimate(感谢JetBrains获得许可) ...
然而单元测试的编写也不是一件容易的事情,除非使用TDD方式,否则编写出容易测试的代码不但对开发人员的设计编码要求很高,而且代码中的各种依赖也常常为单元测试带来无穷无尽的障碍。令人欣慰的是开源社区各种优秀...
PowerMock 也是一个单元测试模拟框架,它是在其它单元测试模拟框架的基础上做出的扩展。通过提供定制的类加载器以及一些字节码篡改技巧的应用,PowerMock 现了对静态方法、构造方法、私有方法以及 Final 方法的模拟...
Unitils对EasyMock的集成使得在单元测试中使用这种模拟技术变得更容易。 总而言之,Unitils通过提供一系列实用工具,使得开发者能够更专注于编写核心的测试逻辑,而不是被测试的基础设施所拖累。它通过提供断言方法...
Jmock 使我们可以轻松地构造 Mock 对象,从而使单元测试顺利进行。 七、JunitPerf 进行性能测试 JunitPerf 是个来度量代码的性能和执行效率的一个性能测试工具,通过编写用于 JunitPerf 的单元测试代码能使这一...
对于简单的业务逻辑,编写单元测试相对容易;而对于依赖外部资源(如数据库)的组件,比如DAO(Data Access Object),编写单元测试则变得更为复杂。 #### 二、DAO组件与单元测试挑战 **1. DAO组件简介** - **...
单元测试实践小结[4]软件测试编写Stubs和Mockobject1.接口的mock比较容易,测试时,编写stubs和mockobject来辅助测试,是非常重要的技术.Mockobject分动态mock和静态mock.采用EasyMock可以很好的实现动态mock。2....
《PowerMock实战手册》是一本专注于使用PowerMock进行单元测试的指南,结合了Junit测试框架和Mockito库,为开发者提供了全面的测试解决方案。在实际的软件开发中,单元测试是确保代码质量的重要环节,它能帮助我们找...
1. **减少耦合**:通过mock技术,可以降低被测代码与其他组件之间的依赖,使单元测试更加纯粹。 2. **提高测试速度**:mock对象通常比真实对象响应更快,有助于加速测试过程。 3. **控制测试环境**:mock对象允许...
Unitils 测试框架目的是让单元测试变得更加容易和可维护。Unitils 构建在DbUnit 与 EasyMock 项目之上并与JUnit 和TestNG 相结合。支持数据库测试,支持利用Mock 对象 进行测试并提供与Spring 和Hibernate 相集成。...