`

看了抛哥的mock留言,找了找easymock,留个笔记,分享给大家!

    博客分类:
  • J2SE
阅读更多

单元测试是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
*/
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()方法,我们需要模拟HttpServletRequestServletContext 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
 */
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();
	}
}

   

 

 

 

 

 

0
0
分享到:
评论

相关推荐

    EasyMock

    EasyMock 是一个强大的Java模拟框架,它允许开发者在单元测试中创建和控制对象的行为,以模拟复杂的依赖关系。这个框架的出现,使得测试代码能够独立于实际的系统环境,提高测试的效率和覆盖率。 EasyMock 的核心...

    Easymock学习笔记

    EasyMock 是一个强大的Java模拟框架,它允许开发者创建模拟对象(Mock Objects)来代替真实对象在单元测试中使用。这极大地简化了复杂系统的测试,因为我们可以控制这些模拟对象的行为,以便在测试过程中专注于代码...

    easyMock resource

    这时,我们可以使用EasyMock创建B的mock对象,设置其返回值或抛出异常,然后在JUnit测试中使用这个mock对象代替真实的B,从而专注于测试类A的行为。 在文件名称列表`testFromwork`中,可能包含了相关的测试代码示例...

    5-Mock+EasyMock.rar

    【标题】"5-Mock+EasyMock.rar" 涉及到的是软件开发中的测试技术,特别是关于模拟对象(Mock Objects)和EasyMock框架的应用。在软件开发中,特别是使用面向对象编程时,Mock对象是一种重要的测试策略,它用于替代...

    EasyMock单元测试例子

    这个"EasyMock单元测试例子"提供了几个示例,帮助我们更好地理解和应用EasyMock。 EasyMock的基本概念: 1. **模拟对象(Mock Object)**:在单元测试中,我们可能不希望依赖实际的外部服务或数据库。模拟对象可以...

    EasyMock的安装与部署所需全部文件.zip

    在这个“EasyMock的安装与部署所需全部文件.zip”压缩包中,包含了在Windows环境下部署EasyMock所需的所有组件和步骤,包括Node.js、Redis、MongoDB以及EasyMock的源码。 1. **Node.js**:Node.js是一个基于Chrome ...

    EasyMock 简介

    它提供对接口的模拟,能够通过录制、回播放三步来完成大体的测试过程,可以验证方法的调用种类、次数、顺序,可以令 Mock 对象返回指定的值或抛出指定异常。通过 EasyMock,我们可以方便的构造 Mock 对象从而使单元...

    EasyMock与Mockito 使用

    EasyMock和Mockito是两个广泛使用的Java模拟框架,它们可以帮助开发者在隔离环境中测试代码,避免了依赖其他组件或服务的复杂性。本文将详细介绍这两个工具的使用方法、原理和差异。 **EasyMock** EasyMock是一个...

    EasyMock入门

    EasyMock 是一个强大的Java模拟框架,它允许开发者在单元测试中创建和控制对象的行为,而无需实际实例化这些对象。这个框架使得测试更加简洁,因为它可以隔离被测试代码,只关注于当前测试的功能,而不是依赖于其他...

    easymock2.4+EasyMock使用简明手册.pdf

    1. **模拟对象(Mock Object)**:EasyMock允许我们创建模拟对象,这些对象可以模仿真实对象的行为,但不会执行实际的业务逻辑。这样,我们可以在测试中专注于代码的单一行为,而不受其他依赖的影响。 2. **期望...

    easymock-4.0-bundle

    手动的构造 Mock 对象会给开发人员带来额外的编码量,而且这些为创建 Mock 对象而编写的代码很有可能引入错误。目前,有许多开源项目对动态构建 Mock 对象提供了支持,这些项目能够根据现有的接口或类动态生成,这样...

    EasyMock是一个可视化并且能快速生成模拟数据的持久化服务

    压缩包文件 "easy-mock-easy-mock-1b577a5" 可能包含了EasyMock的源代码或者特定版本的部署包。通过查看和分析这些文件,开发者可以更深入地理解其内部工作原理,自定义扩展功能,或者在本地运行自己的EasyMock服务...

    easyMock

    EasyMock 是一个强大的Java模拟框架,它允许开发者在单元测试中创建和控制对象的行为,以模拟复杂的依赖关系。这个框架的出现使得测试更加简洁、独立,可以有效地验证代码的正确性,而无需运行实际的依赖服务或库。...

    EasyMock 实例

    EasyMock 是一个强大的Java模拟框架,它允许开发者在单元测试中创建和控制对象的行为,以模拟复杂的系统交互。这个框架的使用可以极大地提高测试的效率和覆盖率,因为它使得测试代码可以独立于实际的依赖进行执行。 ...

    easymock 3.0

    Easymock 3.0 是一个流行的Java模拟框架,用于进行单元测试。它使得开发者能够在测试代码中创建和控制对象的行为,以便于隔离被测试代码并确保其正确性。在单元测试中,Easymock允许我们创建mock对象,这些对象模仿...

    easymock教程

    - **命名Mock对象**:为了提高测试代码的可读性,建议给Mock对象起有意义的名字。 ##### 1.17 使用MockControl - **MockControl**:早期版本的Easymock使用MockControl来创建Mock对象。虽然现在更推荐使用Easymock...

    转:EasyMock 单元测试

    如果在测试期间,mock 对象的调用不符合预设的期望,EasyMock 将抛出一个异常。 ```java verify(myServiceMock); ``` **静态和 final 类的测试** EasyMock 也支持对静态方法和 final 类进行模拟。通过使用 ...

Global site tag (gtag.js) - Google Analytics