- 浏览: 83895 次
- 性别:
- 来自: 北京
Mock 对象能够模拟领域对象的部分行为,并且能够检验运行结果是否和预期的一致。领域类将通过与 Mock 对象的交互,来获得一个独立的测试环境(引自《 精通 Spring——Java 轻量级架构开发实践 》。
在模仿对象中,我们定义了四个概念:
1 )目标对象:正在测试的对象
2 )合作者对象:由目标对象创建或获取的对象
3 )模仿对象:遵循模仿对象模式的合作者的子类(或实现)
4 )特殊化对象:覆盖创建方法以返回模仿对象而不是合作者的目标的子类
一般来说,应用模仿对象的过程如下:
1 )创建模仿对象的实例
2 )设置模仿对象中的状态和期望值
3 )将模仿对象作为参数来调用域代码
4 )验证模仿对象中的一致性
那么,我们应该如何以及在哪里使用 Mock 对象呢?一般来说,对于目标对象中的合作者对象,在测试时如果其状态或行为的实现严重地依赖外部资源(比如数据持久化中的 DAO ,比如负责发送电子邮件的类),或者团队并行开发时,目标对象的合作者对象并没有实现(比如 J2EE 中,横向分工时,负责 Action 的调用 Service ,负责 Service 调用 DAO 时,相应的 Service 及 DAO 没有实现),这时我们就需要模仿这些类。其实,在做 J2EE 时,传统的 N 层架构中,我们都是面向接口编程的,我们定义了 DAO 接口,我们定义了 Service 接口,这样做的优点就是我们在测试时可以构造实现接口的 Mock 类。这里不得不提依赖注入,通过依赖注入,我们才能在测试时 set Mock 对象。这也说明,为了方便测试,我们不得不一步一步 重构代码,而模式就在重构中自然地产生了。
对于 Mock 对象,我们可以根据 合作者接口(或者是类) 实现具体的 Mock 类,这样的 Mock 类实际上是 Stub 。有些情况下, Stub 是必要的。但对于诸如 DAO 、 Service ,我们只关心在给定参数的情况下,调用的方法能够返回预期的值,我们根本不关心其内部实现,这时候如果使用 Stub 的话就会产生不必要的代码。我们需要的就是能 动态地生成 Mock 对象而不需要编写它们的工具, EasyMock 就是这样的工具。
EasyMock 是一个 Mock 对象的类库,现在的版本是 2.0 ,这个版本只支持 Mock 接口,如果需要 Mock 类,需要下载它的扩展包。
下面通过一个具体的例子说明一下 Mock 对象的使用,我写的例子就是测试 Service 类中的一个方法, Mock 的对象是 DAO 。
首先是一个简单的实体 bean Account:
package easymocktest.domain;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
public class Account {
private Long id;
private String name;
private String pwd;
public Long getId() {
return id;
}
public void setId(Long id) {
this .id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this .name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this .pwd = pwd;
}
/** */ /**
* @see java.lang.Object#toString()
*/
public String toString() {
return new ToStringBuilder( this , ToStringStyle.MULTI_LINE_STYLE)
.append( " name " , this .name).append( " pwd " , this .pwd).append( " id " ,
this .id).toString();
}
}
一个只含一个方法的 DAO AccountDAO:
package easymocktest.dao;
import easymocktest.domain. * ;
public interface AccountDAO {
public Account getByNameAndPwd(String name,String pwd);
}
一个同样只含一个方法的 AccountService 接口:
package easymocktest.service;
import easymocktest.domain. * ;
public interface AccountService {
public Account getAccount(String name,String pwd);
}
与 AccountService 相应的实现类:
package easymocktest.service.impl;
import easymocktest.service.AccountService;
import easymocktest.dao. * ;
import easymocktest.domain.Account;
public class AccountServiceImpl implements AccountService {
private AccountDAO accountDAO;
public AccountDAO getAccountDAO() {
return accountDAO;
}
public void setAccountDAO(AccountDAO accountDAO) {
this .accountDAO = accountDAO;
}
public Account getAccount(String name, String pwd) {
return this .accountDAO.getByNameAndPwd(name, pwd);
}
}
这里我没有实现 AccountDAO 接口,对于 Mock 测试来说,这也是不需要的。下面就是 AccountServiceImpl 的测试类 AccountServiceTest :
package easymocktest.service;
import junit.framework. * ;
import easymocktest.dao. * ;
import easymocktest.domain. * ;
import easymocktest.service.impl. * ;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import static org.easymock.EasyMock.expect;
public class AccountServiceTest extends TestCase {
private AccountDAO accountDAOMock;
private AccountServiceImpl accountService;
@Override
protected void setUp() throws Exception {
accountDAOMock = createMock(AccountDAO. class );
accountService = new AccountServiceImpl();
accountService.setAccountDAO(accountDAOMock);
}
public void testGetAccount() {
String name = " kafka " ;
String pwd = " 0102 " ;
Account a = new Account();
a.setName(name);
a.setPwd(pwd);
a.setId( new Long( 10 ));
reset(accountDAOMock); // (a)
expect(accountDAOMock.getByNameAndPwd(name, pwd)).andReturn(a); // (b)
replay(accountDAOMock); // (c)
Account b = accountService.getAccount(name, pwd);
assertEquals(a, b);
verify(accountDAOMock); // (d)
}
}
下面简要的说明一下 Mock 对象的工作过程:
1 )在 setUp() 中,通过 “accountDAOMock = createMock(AccountDAO.class);” (这里使用了 java5 中的静态导入),创建 AccountDAO 的 Mock 对象,由于 EasyMock 采用了范型技术,故创建的 Mock 对象不需要强制类型转换。然后通过 “accountService.setAccountDAO(accountDAOMock);” 设置目标对象的合作者对象。
2 )对于测试方法 “testGetAccount()” , (a) 处的 reset() 方法是将 Mock 对象复位,也就是重新设置 Mock 对象的状态和行为。由于此处是第一次调用 Mock 对象,可以不必使用 reset() 方法。
3 ) (b) 处 expect() 是录制 Mock 对象方法的调用,其参数就是 Mock 对象的方法,其中如果调用的方法有返回值,要通过 andReturn() 方法设置预期的返回值。
4 ) (c) 处的 replay() 是结束录制过程。 在调用 replay() 方法之前的状态, EashMock 称之为 “record 状态 ” 。该状态下, Mock 对象不具备行为(即模拟接口的实现),它仅仅记录方法的调用。在调用 replay() 后,它才以 Mock 对象预期的行为进行工作,检查预期的方法调用是否真的完成。
5 ) (d) 处的 verify() 是用于在录制和回放两个步骤完成之后进行预期和实际结果的检查。这里就是检查 accountDAOMock 是否如预期一样调用了 getByNameAndPwd 方法。
对于上面的举例,它可能并不具有实际的价值,这里我只想抛砖引玉。在 N 层架构的 Java 程序中, Mock 对象在单元测试中正发挥着越来越重要的作用。我现在看到的是,在 Service 层与 Web 层, Mock 对象能很好的被应用。有人觉得在 Persistence 层也应该使用 Mock 对象,但就像我们所知道的,在使用 Hibernate 、 Ibatis 等 ORM 工具的情况下,我们的 Persistence 层的测试主要测试的就是那些配置文件、查询语句等(实际上是集成测试),如果还 Mock 的话,就失去了测试的意义。
在模仿对象中,我们定义了四个概念:
1 )目标对象:正在测试的对象
2 )合作者对象:由目标对象创建或获取的对象
3 )模仿对象:遵循模仿对象模式的合作者的子类(或实现)
4 )特殊化对象:覆盖创建方法以返回模仿对象而不是合作者的目标的子类
一般来说,应用模仿对象的过程如下:
1 )创建模仿对象的实例
2 )设置模仿对象中的状态和期望值
3 )将模仿对象作为参数来调用域代码
4 )验证模仿对象中的一致性
那么,我们应该如何以及在哪里使用 Mock 对象呢?一般来说,对于目标对象中的合作者对象,在测试时如果其状态或行为的实现严重地依赖外部资源(比如数据持久化中的 DAO ,比如负责发送电子邮件的类),或者团队并行开发时,目标对象的合作者对象并没有实现(比如 J2EE 中,横向分工时,负责 Action 的调用 Service ,负责 Service 调用 DAO 时,相应的 Service 及 DAO 没有实现),这时我们就需要模仿这些类。其实,在做 J2EE 时,传统的 N 层架构中,我们都是面向接口编程的,我们定义了 DAO 接口,我们定义了 Service 接口,这样做的优点就是我们在测试时可以构造实现接口的 Mock 类。这里不得不提依赖注入,通过依赖注入,我们才能在测试时 set Mock 对象。这也说明,为了方便测试,我们不得不一步一步 重构代码,而模式就在重构中自然地产生了。
对于 Mock 对象,我们可以根据 合作者接口(或者是类) 实现具体的 Mock 类,这样的 Mock 类实际上是 Stub 。有些情况下, Stub 是必要的。但对于诸如 DAO 、 Service ,我们只关心在给定参数的情况下,调用的方法能够返回预期的值,我们根本不关心其内部实现,这时候如果使用 Stub 的话就会产生不必要的代码。我们需要的就是能 动态地生成 Mock 对象而不需要编写它们的工具, EasyMock 就是这样的工具。
EasyMock 是一个 Mock 对象的类库,现在的版本是 2.0 ,这个版本只支持 Mock 接口,如果需要 Mock 类,需要下载它的扩展包。
下面通过一个具体的例子说明一下 Mock 对象的使用,我写的例子就是测试 Service 类中的一个方法, Mock 的对象是 DAO 。
首先是一个简单的实体 bean Account:
package easymocktest.domain;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
public class Account {
private Long id;
private String name;
private String pwd;
public Long getId() {
return id;
}
public void setId(Long id) {
this .id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this .name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this .pwd = pwd;
}
/** */ /**
* @see java.lang.Object#toString()
*/
public String toString() {
return new ToStringBuilder( this , ToStringStyle.MULTI_LINE_STYLE)
.append( " name " , this .name).append( " pwd " , this .pwd).append( " id " ,
this .id).toString();
}
}
一个只含一个方法的 DAO AccountDAO:
package easymocktest.dao;
import easymocktest.domain. * ;
public interface AccountDAO {
public Account getByNameAndPwd(String name,String pwd);
}
一个同样只含一个方法的 AccountService 接口:
package easymocktest.service;
import easymocktest.domain. * ;
public interface AccountService {
public Account getAccount(String name,String pwd);
}
与 AccountService 相应的实现类:
package easymocktest.service.impl;
import easymocktest.service.AccountService;
import easymocktest.dao. * ;
import easymocktest.domain.Account;
public class AccountServiceImpl implements AccountService {
private AccountDAO accountDAO;
public AccountDAO getAccountDAO() {
return accountDAO;
}
public void setAccountDAO(AccountDAO accountDAO) {
this .accountDAO = accountDAO;
}
public Account getAccount(String name, String pwd) {
return this .accountDAO.getByNameAndPwd(name, pwd);
}
}
这里我没有实现 AccountDAO 接口,对于 Mock 测试来说,这也是不需要的。下面就是 AccountServiceImpl 的测试类 AccountServiceTest :
package easymocktest.service;
import junit.framework. * ;
import easymocktest.dao. * ;
import easymocktest.domain. * ;
import easymocktest.service.impl. * ;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import static org.easymock.EasyMock.expect;
public class AccountServiceTest extends TestCase {
private AccountDAO accountDAOMock;
private AccountServiceImpl accountService;
@Override
protected void setUp() throws Exception {
accountDAOMock = createMock(AccountDAO. class );
accountService = new AccountServiceImpl();
accountService.setAccountDAO(accountDAOMock);
}
public void testGetAccount() {
String name = " kafka " ;
String pwd = " 0102 " ;
Account a = new Account();
a.setName(name);
a.setPwd(pwd);
a.setId( new Long( 10 ));
reset(accountDAOMock); // (a)
expect(accountDAOMock.getByNameAndPwd(name, pwd)).andReturn(a); // (b)
replay(accountDAOMock); // (c)
Account b = accountService.getAccount(name, pwd);
assertEquals(a, b);
verify(accountDAOMock); // (d)
}
}
下面简要的说明一下 Mock 对象的工作过程:
1 )在 setUp() 中,通过 “accountDAOMock = createMock(AccountDAO.class);” (这里使用了 java5 中的静态导入),创建 AccountDAO 的 Mock 对象,由于 EasyMock 采用了范型技术,故创建的 Mock 对象不需要强制类型转换。然后通过 “accountService.setAccountDAO(accountDAOMock);” 设置目标对象的合作者对象。
2 )对于测试方法 “testGetAccount()” , (a) 处的 reset() 方法是将 Mock 对象复位,也就是重新设置 Mock 对象的状态和行为。由于此处是第一次调用 Mock 对象,可以不必使用 reset() 方法。
3 ) (b) 处 expect() 是录制 Mock 对象方法的调用,其参数就是 Mock 对象的方法,其中如果调用的方法有返回值,要通过 andReturn() 方法设置预期的返回值。
4 ) (c) 处的 replay() 是结束录制过程。 在调用 replay() 方法之前的状态, EashMock 称之为 “record 状态 ” 。该状态下, Mock 对象不具备行为(即模拟接口的实现),它仅仅记录方法的调用。在调用 replay() 后,它才以 Mock 对象预期的行为进行工作,检查预期的方法调用是否真的完成。
5 ) (d) 处的 verify() 是用于在录制和回放两个步骤完成之后进行预期和实际结果的检查。这里就是检查 accountDAOMock 是否如预期一样调用了 getByNameAndPwd 方法。
对于上面的举例,它可能并不具有实际的价值,这里我只想抛砖引玉。在 N 层架构的 Java 程序中, Mock 对象在单元测试中正发挥着越来越重要的作用。我现在看到的是,在 Service 层与 Web 层, Mock 对象能很好的被应用。有人觉得在 Persistence 层也应该使用 Mock 对象,但就像我们所知道的,在使用 Hibernate 、 Ibatis 等 ORM 工具的情况下,我们的 Persistence 层的测试主要测试的就是那些配置文件、查询语句等(实际上是集成测试),如果还 Mock 的话,就失去了测试的意义。
发表评论
-
敏捷软件开发
2016-08-19 16:32 334敏捷软件开发 敏捷软件开发是一种面临迅速变化的需求快速开 ... -
XML 系列教程
2012-05-06 12:50 612http://www.w3school.com.cn/x.as ... -
MANIFEST.MF是个啥?请给俺个详细解答
2012-02-06 17:48 1052看完下面的帖子你就明 ... -
如何从IT民工跃身成精英
2011-12-19 15:11 804“IT”这个英文缩写 ... -
java_ant详解(转载)
2011-12-17 14:40 12571,什么是ant ant是构建工具 2,什么是构建 概念到处可 ... -
Log4j使用总结
2011-12-14 16:10 1一、介绍 Log4j是Apache的一个开放源代码项目,通过 ... -
Log4j使用总结(转)
2011-12-14 16:08 699一、介绍 Log4j是Apache的一个开放源代码项目,通过 ... -
关于正则表达式的贪婪与非贪婪模式
2011-11-20 15:42 1147以前看正则表达式,但没有注意到正则表达式的贪婪与非贪婪模式,今 ... -
MyEclipse内存不足以及参数配置“ -Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M ”
2011-11-19 20:20 2536“MyEclipse has detected that ... -
eclipse中的.project 和 .classpath文件的具体作用 (转)
2011-03-10 15:27 1245.project是项目文件,项目的结构都在其中定义,比如lib ... -
Xpath使用(转载)
2010-08-17 18:38 1086XPath 表达式比繁琐的文档对象模型(DOM)导航代码要容易 ... -
XPath简介(转载)
2010-08-17 18:30 894XPath 简介Previous Page Next Page ... -
根据XML内容设置XSL样式表(转载)
2010-08-17 17:36 1089使用可扩展样式表语言转换(Extensible Stylesh ... -
XSL简介(转载)
2010-08-17 17:35 963XSL 语言 Previous Page Next Page ... -
JUnit测试框架使用介绍
2010-08-13 13:35 853JUnit测试骨架 使用JUnit时,主要都是通过继承Test ... -
EasyMock 使用方法与原理剖析
2010-08-11 18:23 821Mock 方法是单元测试中 ... -
JUNIT入门
2010-08-11 15:32 1082关键字: junit test 1、将junit的jar包放 ...
相关推荐
郑 闽睿 (zhengmr@cn.ibm.com), 软件工程师, IBM...EasyMock单元测试的扩展; EasyMock简介(抽象类接口做测试); EasyMock来进行测试; EasyMock如何打桩; EasyMock实践指南;...EasyMock使用手记; 用Mock object进行隔离测试;
在这个"EasyMock 使用案例(含lib)"中,我们将会深入探讨如何利用EasyMock进行Java单元测试,并通过具体的例子——EasyMockDemo来讲解其用法。 首先,EasyMock的基本理念是让你能够定义模拟对象的行为,比如它应该...
EasyMock单元测试的扩展; EasyMock简介(抽象类接口做测试); EasyMock来进行测试; EasyMock如何打桩; EasyMock实践指南; EasyMock使用技巧; EasyMock使用简明手册;...EasyMock使用手记; 用Mock object进行隔离测试;
EasyMock是Java编程中的一款强大的模拟框架,它允许开发者在单元测试中创建和控制...通过阅读《EasyMock使用简明手册》和解压后的easymock2.4.zip文件,开发者可以深入理解并掌握如何有效地使用EasyMock进行单元测试。
【EasyMock使用方法】 1. **创建模拟对象**:首先,需要通过`EasyMock.createMock()`方法创建模拟对象,指定需要模拟的接口。 2. **预设行为**:使用`expect()`方法设定模拟对象的方法调用应如何响应,例如返回...
本文将对 EasyMock 的功能和原理进行介绍,并通过示例来说明如何使用 EasyMock 进行单元测试。 Mock 方法是单元测试中常见的一种技术,它的主要作用是模拟一些在应用中不容易构造或者比较复杂的对象,从而把测试与...
在实际使用EasyMock时,我们通常会遵循以下步骤: 1. **创建模拟对象**:首先,你需要通过`EasyMock.createMock()`方法创建一个模拟对象。例如,如果你有一个名为`MyInterface`的接口,你可以这样创建它的模拟对象...
本文将深入探讨EasyMock的使用方法及其工作原理,帮助开发者更好地理解和运用这一工具。 ### EasyMock的基本概念 - **模拟对象(Mock Object)**:在测试中,模拟对象是替代真实对象的类,它会根据预定义的行为来...
EasyMock和Mockito是两个广泛使用的Java模拟框架,它们可以帮助开发者在隔离环境中测试代码,避免了依赖其他组件或服务的复杂性。本文将详细介绍这两个工具的使用方法、原理和差异。 **EasyMock** EasyMock是一个...
- **创建模拟对象**:你可以使用`EasyMock.createMock()`方法创建一个模拟对象,然后通过`EasyMock.replay()`进入“播放”模式,让模拟对象按照预期执行。 - **设置预期**:使用`expect()`方法设置对模拟对象的期望...
例如,使用`@Before`注解设置测试前的准备工作,使用`@Test`注解定义测试方法,并在其中使用EasyMock的API进行模拟和验证。 总之,EasyMock 3.2 提供了一个强大而灵活的模拟工具,帮助开发者编写高质量的单元测试,...
2. **创建模拟对象**:使用Easymock的`createMock`或`createNiceMock`方法创建模拟对象。 3. **设置预期**:通过`expect`方法设定模拟对象的方法调用预期。 4. **启动测试**:调用`replay`方法开始执行模拟。 5. ...
还在为EasyMock使用时出异常而烦恼? 本压缩包包含除了Junit4之外easyMock3.1所用到的所有相关jar包,junit4可自己导入eclipse自带的即可 本压缩包包括: asm.jar cglib.jar objenesis.jar等 其中asm与cglib已兼容,放心...
为了部署和使用EasyMock,你需要将源码导入一个支持Java开发的IDE(如Eclipse或IntelliJ IDEA),然后按照提供的说明进行编译和配置。 在部署流程中,一般步骤如下: 1. 安装Node.js:访问官网下载Windows安装包,...
**Easymock使用示例** 1. **导入库**:首先,在项目中引入Easymock库,通常通过Maven或Gradle等构建工具完成。 2. **创建模拟对象**:使用`EasyMock.createMock()`方法创建模拟对象,如`MyInterface mockObject = ...