`
ray_yui
  • 浏览: 220591 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

EasyMock使用

阅读更多

前言:本文章需要JUnit单元测试框架的基础知识,若读者还不具备,请阅读笔者的JUnit文章:http://ray-yui.iteye.com/blog/1914106



UnitTest系列文章:
      使用JUnit开发单元测试:http://ray-yui.iteye.com/blog/1914106
      使用DBUnit扩展JUnit:http://ray-yui.iteye.com/blog/1914979
      使用Cactus测试Servlethttp://ray-yui.iteye.com/blog/1917515
      使用Spring TestContext测试Spring应用http://ray-yui.iteye.com/blog/1921424
      使用Cobertura生成测试覆盖率报告http://ray-yui.iteye.com/blog/1921958



什么是EasyMock?
      EasyMock是一套提供了通过简单的接口和方法对给定的对象生成Mock对象的类库,通过record,replay,verify 3个生命周期来帮助完成测试过程,通过EasyMock我们可以方便构造Mock对象从而进行单元测试的开发

为什么要使用EasyMock?
      当我们编写单元测试的过程中,我们常常遇到应用中其他依赖模块尚未开发完成,或者该依赖的构建比较复杂的情况,例如Service层已经开发完成,DAO层却还在开发当中,但Service需要依赖DAO来进行测试,显然这种情况下Service是没有办法进行测试的,因为此时需要依赖DAO进行测试,又或者例如测试Servlet,Request和Session等都需要由服务器来生成,而Mock对象就是用来对一些未实现的关联对象或依赖对象的类进行测试的对象,EasyMock就是实现Mock对象的框架,例如Service依赖DAO,我们可以使用Mock对象来模拟DAO的实现或者模拟Request和Session,简单可以理解为Mock对象是模拟了我们给定接口实现的对象

EasyMock使用:
      首先增加Maven的依赖

<dependency>
	<groupId>org.easymock</groupId>
	<artifactId>easymock</artifactId>
	<version>3.2</version>
	<scope>test</scope>
</dependency>


Mock对象的生命周期:
     1.record  --> 对Mock对象进行关系的说明,交互的过程和可能产生的结果
     2.replay  --> 进入了发布阶段,也就是测试阶段
     3.verify  --> 验证交互的关系是否正确

      在我们开发的过程当中,应该首先定义的是接口,现在假设我们已经定义了DAO层的接口,但实现还没有开发出来,此时要进行Service层的测试,以下为DAO的接口

package com.accentrix.ray;


public interface UserDao {

	// 通过用户名获取用户
	User getBy(String username);

	// 添加用户
	void add(User user);
}


      以下为UserServiceImpl

package com.accentrix.ray;

public class UserServiceImpl implements UserService {

	private UserDao userDao;

	public UserServiceImpl() {

	}

	public UserServiceImpl(UserDao userDao) {
		this.userDao = userDao;
	}

	public User login(String username,String password) {
		if (username == null)
			throw new RuntimeException("用户名为空");
		if (password ==null)
		    throw new RuntimeException("密码为空");
		User loginUser = userDao.getBy(username);
		if (loginUser == null)
			throw new RuntimeException("不存在用户");
		else
			if(!password.equals(loginUser.getPassword()))
				throw new RuntimeException("密码错误");
			else
				return loginUser;
				
	}

	public void add(User user) {
		if(user==null)
			throw new RuntimeException("用户为空");
		else
			userDao.add(user);
	}

}



      以下为测试类

package com.accentrix.ray;

import org.easymock.EasyMock;
import org.easymock.IMocksControl;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class TestUserService {

	private User admin;
	private UserDao userDao;
	private UserService userService;

	@Before
	public void setUp() {
		// 初始化一个用户
		admin = new User(1, "Ray", "123");
		// 创建UserDao的Mock对象
		userDao = EasyMock.createMock(UserDao.class);
		userService = new UserServiceImpl(userDao);
	}

	@Test
	public void testLogin() {

		/*
		 * 以下开始进入record阶段
		 */

		// 此时说明当在Dao中调用get方式时并且参数为Ray的时候应该返回1个用户对象给我
		EasyMock.expect(userDao.getBy("Ray")).andReturn(admin);

		/*
		 * 以下进入replay阶段
		 */

		// 将使用userDao创建的Mock对象传入
		EasyMock.replay(userDao);

		// 直接调用 接口的方法
		User loginUser = userService.login("Ray","123");

		// 使用断言判断是否为空
		Assert.assertNotNull(loginUser);

		/*
		 * 进入verify阶段
		 */
		EasyMock.verify(userDao);
	}

	@Test
	public void testAdd() {
		
		/*
		 * 首先理清测试add方法的思路,先通过UserDao插入一条数据
		 * 然后通过UserDao的get方法来获取,验证是否成功插入 
		 */
		
		
		/*
		 * 在EasyMock最新版本中,已经不推荐使用EasyMock.cerateMock()
		 * 的方式创建Mock对象,而是采用以下方式,以下创建方法和testGet的
		 * 效果完全一样,但注意此时创建的是StrictControl
		 */
		IMocksControl mc = EasyMock.createStrictControl();

		userDao = mc.createMock(UserDao.class);

		/*
		 * 当userDao的Mock对象是返回值,例如get(),getAll()的时候可以
		 * 使用EasyMock.expect(),但当该方法没有返回值,例如add()的时候,
		 * 应该要如何编写呢?请留意以下代码
		 */
		userDao.add(admin);
		EasyMock.expectLastCall();

		/*
		 * 为userDao注册两次事件,注意是必须的,有多少次交互,就需要有
		 * 多少次注册,每次注册都必须对应
		 */
		EasyMock.expect(userDao.getBy("Ray")).andReturn(admin);

		EasyMock.replay(userDao);

		userDao.add(admin);
		User addUser = userDao.getBy("Ray");
		Assert.assertNotNull(addUser);

		EasyMock.verify(userDao);
		
		/*
		 * reset可以将Control进行重置,方便MockControl的重用,
		 * 我们可以在@After中使用
		 */
		mc.reset();

	}
}


      可以留意到上面的testGet和testAdd获取Mock对象时,testGet是使用createMock,而testAdd()是使用createStrictMock,两者究竟有什么区别呢?就用testAdd中作为例子,testAdd中需要执行2次UserDao的方法,分别是add和get,此时若然使用createMock是不会检查他们之间的顺序,那么就可以先执行get再执行add,但使用createStrictMock就要检查顺序是否正确,而生命周期中的verify就是进行验证的操作.


	@Test
	public void testAdd() {

		/*
		 * 注意此处为createNiceControl,和createMockControl和createStrictMock
		 * 的分别为,在使用NiceControl时若然调用的方法没有注册,仍然可以成功调用
		 * ,不过会返回0,null,false的友好值,但不建议使用此种方式
		 */
		IMocksControl mc = EasyMock.createNiceControl();

		userDao = mc.createMock(UserDao.class);

		// times代表注册2次getBy('Ray')的方法
		EasyMock.expect(userDao.getBy("Ray")).times(2);

		// times代表注册2次或以上 5次或以下的方法
		EasyMock.expect(userDao.getBy("Admin")).times(2, 5);

		// 有时当运行某个方法需要抛出异常时,可以使用andThrow()
		EasyMock.expect(userDao.getAll()).andThrow(
				new NullPointerException("test"));


	}


      EasyMock参数匹配器

	@Test
	public void testAdd() {

		IMocksControl mc = EasyMock.createNiceControl();

		userDao = mc.createMock(UserDao.class);

		/*
		 * 在使用 Mock 对象进行实际的测试过程中,EasyMock会根据方法名和
		 * 参数来匹配一个预期方法的调用.这里就造成了若然实际调用不是输入Ray
		 * 就不能成功调用方法的情况,这就需要用到EasyMock中的参数匹配器
		 */
		
		//实际调用时传入任何字符串都可以成功调用,EasyMock.anyString();
		EasyMock.expect(userDao.getBy(EasyMock.anyString()));
		
		//当传入参数包含R时成功调用
		EasyMock.expect(userDao.getBy(EasyMock.contains("R")));
		
		//当传入参数满足正则表达式时调用
		EasyMock.expect(userDao.getBy(EasyMock.matches("正则表达式")));
		
		/*
		 * 由于EasyMock提供非常多的参数匹配器,笔者这里就不一一列举了
		 * 在实际应用中看情况使用,参数匹配器的名称亦非常有语义,不难掌握
		 */
		

	}


      使用EasyMock模拟Servlet容器进行测试

package com.accentrix.ray;

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

import org.easymock.EasyMock;
import org.easymock.IMocksControl;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class TestLoginServlet {

	// 声明要测试的loginServlet和session and request
	private LoginServlet loginServlet;
	private HttpSession session;
	private HttpServletRequest request;
	private IMocksControl mc;

	@Before
	public void setUp() {
		//init mc session and request object
		mc = EasyMock.createStrictControl();
		session = mc.createMock(HttpSession.class);
		request = mc.createMock(HttpServletRequest.class);
	}

	@Test
	public void testSessionIsNull() {
		
		//注册关系,事件
		EasyMock.expect(request.getSession()).andReturn(null);
		EasyMock.replay(request, session);
		Assert.assertNot(loginServlet.login(request));
		EasyMock.verify(request,session);
	}

}


总结:
      通过使用EasyMock可以有效的在关联或依赖类没提供实现的情况下编写单元测试,而且还能解除单元测试的耦合,可以试想一下,当我们使用EasyMock编写Service层的单元测试时,可以完全不用考虑DAO层的实现,而且成功有效的阻断了对数据库的影响,而DAO层的测试,应该由DAO的测试用例进行测试,从而把Service和DAO的测试完全分离了.EasyMock还可以测试简单的Servlet,但EasyMock也有无力的地方,例如当Servlet跳转(forward/redirect)的时候无法验证到跳转的页面是否正确,又例如在后台将json通过print的方式打印到前台的时候也无法捕获print的值.
2
1
分享到:
评论
2 楼 ray_yui 2013-07-31  
diaozhanming 写道
EasyMock如果能搭配Powermock使用,会发挥更强大的威力。另外后边举的两个例子,可能不是那么恰当了,这样的测试已经不是unit test的范围,需要结合功能测试一起进行,比如Selenium

非常感谢你的评论和指导,不足的地方会更加注意,再次感谢
1 楼 diaozhanming 2013-07-31  
EasyMock如果能搭配Powermock使用,会发挥更强大的威力。另外后边举的两个例子,可能不是那么恰当了,这样的测试已经不是unit test的范围,需要结合功能测试一起进行,比如Selenium

相关推荐

    EasyMock 使用案例(含lib)

    在这个"EasyMock 使用案例(含lib)"中,我们将会深入探讨如何利用EasyMock进行Java单元测试,并通过具体的例子——EasyMockDemo来讲解其用法。 首先,EasyMock的基本理念是让你能够定义模拟对象的行为,比如它应该...

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

    EasyMock是Java编程中的一款强大的模拟框架,它允许开发者在单元测试中创建和控制...通过阅读《EasyMock使用简明手册》和解压后的easymock2.4.zip文件,开发者可以深入理解并掌握如何有效地使用EasyMock进行单元测试。

    EasyMock介绍和使用

    【EasyMock使用方法】 1. **创建模拟对象**:首先,需要通过`EasyMock.createMock()`方法创建模拟对象,指定需要模拟的接口。 2. **预设行为**:使用`expect()`方法设定模拟对象的方法调用应如何响应,例如返回...

    EasyMock 使用方法与原理剖析.rar

    本文将深入探讨EasyMock的使用方法及其工作原理,帮助开发者更好地理解和运用这一工具。 ### EasyMock的基本概念 - **模拟对象(Mock Object)**:在测试中,模拟对象是替代真实对象的类,它会根据预定义的行为来...

    easymock资料和源代码实例

    **Easymock使用示例** 1. **导入库**:首先,在项目中引入Easymock库,通常通过Maven或Gradle等构建工具完成。 2. **创建模拟对象**:使用`EasyMock.createMock()`方法创建模拟对象,如`MyInterface mockObject = ...

    Easymock学习笔记

    10. **EasyMock使用方法**:在编写测试用例时,典型的做法是首先设定Mock对象的期望,然后调用待测试的方法,最后验证Mock对象的行为是否符合预期,如 `expect(mockEmployeeRepository.findByFirstNameAndLastName(...

    easymock教程

    - **MockControl**:早期版本的Easymock使用MockControl来创建Mock对象。虽然现在更推荐使用Easymock的新API,但了解MockControl的基本用法仍然有助于理解Mock对象的工作原理。 以上内容是对Easymock教程的深入解析...

    EasyMock用到的objenesis

    EasyMock 使用Objenesis 来快速实例化模拟对象,特别是在那些没有默认构造函数或者构造函数有特定初始化需求的类中。 Objenesis 的核心功能在于它的`Instantiator`接口,提供了多种策略来实例化对象。例如,`...

    easymock-request.getParamsNames

    在这些测试方法中,你会看到上面描述的Easymock使用示例。 另一方面,`HttpServletRequest的属性_百度知道.mht` 文件可能是一个包含了关于 `HttpServletRequest` 接口更详细信息的资料,例如它提供的其他方法和属性...

    easyMock.zip

    这个“easyMock.zip”文件包含了一个关于EasyMock使用的教程,特别是它的典型应用,对于理解和掌握EasyMock在实际开发中的应用非常有帮助。 首先,EasyMock的基本概念是模拟对象。在单元测试中,我们通常需要测试一...

    EasyMock 3.1相关jar(所有)

    还在为EasyMock使用时出异常而烦恼? 本压缩包包含除了Junit4之外easyMock3.1所用到的所有相关jar包,junit4可自己导入eclipse自带的即可 本压缩包包括: asm.jar cglib.jar objenesis.jar等 其中asm与cglib已兼容,放心...

    EasyMock 教程

    EasyMock使用教程,快来看看你还有什么秘密没有发现吧!

    easymock的使用,含demo

    本文将对 EasyMock 的功能和原理进行介绍,并通过示例来说明如何使用 EasyMock 进行单元测试。 Mock 方法是单元测试中常见的一种技术,它的主要作用是模拟一些在应用中不容易构造或者比较复杂的对象,从而把测试与...

    EasyMock

    在实际使用EasyMock时,我们通常会遵循以下步骤: 1. **创建模拟对象**:首先,你需要通过`EasyMock.createMock()`方法创建一个模拟对象。例如,如果你有一个名为`MyInterface`的接口,你可以这样创建它的模拟对象...

    EasyMockTest

    三、EasyMock使用步骤 1. 创建模拟对象:使用`EasyMock.createMock()`方法创建一个模拟对象。 2. 预定义行为:使用`expect()`方法指定模拟对象的方法调用预期,包括返回值、参数匹配等。 3. 初始化模拟对象:使用`...

Global site tag (gtag.js) - Google Analytics