`
ray_yui
  • 浏览: 217521 次
  • 性别: 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做java单元测试的例子,包含所需要的jar包

    EasyMock 使用方法与原理剖析

    EasyMock单元测试的扩展; EasyMock简介(抽象类接口做测试); EasyMock来进行测试; EasyMock如何打桩;...EasyMock使用技巧; EasyMock使用简明手册; EasyMock使用说明; EasyMock使用手记; 用Mock object进行隔离测试;

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

    easymock2 EasyMock使用简明手册

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

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

    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

    Easy Mock 详解

    EasyMock单元测试的扩展; EasyMock简介(抽象类接口做测试); EasyMock来进行测试; EasyMock如何打桩;...EasyMock使用技巧; EasyMock使用简明手册; EasyMock使用说明; EasyMock使用手记; 用Mock object进行隔离测试;

    EasyMock介绍和使用

    IBM网站对EasyMock和使用场景的一些介绍

    模拟测试辅助工具easyMock.zip

    EasyMock 是一套通过简单的方法对于指定的接口或类生成 Mock 对象的类库,它能利用对接口或类的模拟来辅助...本文将向您展示如何使用 EasyMock 进行单元测试,并对 EasyMock 的原理进行分析。 标签:easyMock

    easymock-3.2.jar

    EasyMock主要是为测试提供模拟数据,比如你可以模拟HttpServletRequest。

    easyMock2.2.doc

    easyMock2.2

    easymock.jar,easymockclassextension.jar

    还在为找不到jar文件烦心吗,不用了到我空间来有你想要的,持续更新。。。 easymock.jar,easymockclassextension.jar

    easymock-3.1.jar

    easymock需要用到的包,没有它不行,easymock-3.1.jar

    easymock-4.2.jar

    EasyMock 是一套通过简单的方法对于指定的接口或类生成 Mock 对象的类库,它能利用对接口或类的模拟来辅助单元测试。 Mock 方法是单元测试中常见的一种技术,它的主要作用是模拟一些在应用中不容易构造或者比较复杂...

Global site tag (gtag.js) - Google Analytics