EasyMock使用说明
来自官网的使用说明,原文见http://www.easymock.org/EasyMock2_0_Documentation.html
1.1. 准备
大多数的软件系统都不是单独运行的,它们都需要于其他部分系统合作,来完成工作。大多数情况下,我们在进行单元测试时,不会担心其他部分,而是假定它们都会工作良好。如果我们需要考虑其他部分的情况,Mock对象可以帮助我们对某一单元,进行隔离测试。Mock对象将在测试中代替合作者。
下边的例子是一个Collaborator接口:
package org.easymock.samples;
public interface Collaborator {
void documentAdded(String title);
void documentChanged(String title);
void documentRemoved(String title);
byte voteForRemoval(String title);
byte[] voteForRemovals(String[] title);
}
实现了这个接口的类(在这里都是监听器),是一个叫做ClassUnderTest类的合作者:
public class ClassUnderTest {
//...
public void addListener(Collaborator listener) {
//...
}
public void addDocument(String title, byte[] document) {
//...
}
public boolean removeDocument(String title) {
//...
}
public boolean removeDocuments(String[] titles) {
//...
}
}
这些代码都可以在easymock.zip的sample.zip中找到,包名为org.easymock.samples
下面的例子假设你熟悉JUnit测试框架。虽然这些测试使用了JUnit 3.8.1,你也可以使用JUnit 4或TestNG
1.2. 第一个Mock对象
我们现在就要写一个测试用例,然后根据它来了解EasyMock包的结构。samples.zip里包含了这个测试的修改版。我们的第一个测试,应该检测,一个不存在的document的removal操作是否会告诉那些合作者。这里的测试,没有定义Mock对象:
package org.easymock.samples;
import junit.framework.TestCase;
public class ExampleTest extends TestCase {
private ClassUnderTest classUnderTest;
private Collaborator mock;
protected void setUp() {
classUnderTest = new ClassUnderTest();
classUnderTest.addListener(mock);
}
public void testRemoveNonExistingDocument() {
// This call should not lead to any notification
// of the Mock Object:
classUnderTest.removeDocument("Does not exist");
}
}
对于大多数使用EasyMock 2的测试,我们只要使用静态导入org.easymock.EasyMock的所有方法。这是EasyMock 2中唯一没有内在方法,也没有废弃方法的类。
import static org.easymock.EasyMock.*;
import junit.framewok.TestCase;
public class ExampleTest extends TestCase {
private ClassUnderTest classUnderTest;
private Collaborator mock;
}
为了获得Mock对象,我们需要
- 为我们想使用的接口,创建一个Mock对象。
- 录制预期的行为,然后
- 将Mock对象转换到replay(重放)状态
这里有一个小例子:
protected void setUp() {
mock = createMock(Collaborator.class); // 1
classUnderTest = new ClassUnderTest();
classUnderTest.addListener(mock);
}
public void testRemoveNonExistingDocument() {
// 2 (we do not expect anything)
replay(mock); // 3
classUnderTest.removeDocument("Does not exist");
}
完成第三步后,mock是一个Collaborator接口的Mock对象,它不希望有任何的调用。这意味着,如果我们改变了ClassUnderTest,调用了接口的任何一个方法,Mock对象就回抛出一个AssertionError:
java.lang.AssertionError:
Unexpected method call documentRemoved("Does not exist"):
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
at $Proxy0.documentRemoved(Unknown Source)
at org.easymock.samples.ClassUnderTest.notifyListenersDocumentRemoved(ClassUnderTest.java:74)
at org.easymock.samples.ClassUnderTest.removeDocument(ClassUnderTest.java:33)
at org.easymock.samples.ExampleTest.testRemoveNonExistingDocument(ExampleTest.java:24)
...
1.3. 添加行为
让我们开始写第二个测试。如果一个document添加到ClassUnderTest中,我们希望mock.documentAdded()会被调用,并把document的标题作为参数。
public void testAddDocument() {
mock.documentAdded("New Document"); // 2
replay(mock); // 3
classUnderTest.addDocument("New Document", new byte[0]);
}
在录制环节(调用replay之前),Mock对象没有办法像Mock对象那样工作,但是它记录了方法调用。在调用replay之后,它就可以使用了。检查是否出现了对期望的方法的调用。
如果ClassUnderTest.addDocument("New Document", new byte[0])调用了预期的方法,但使用了错误参数,Mock对象会抛出一个AssertionErro:
java.lang.AssertionError:
Unexpected method call documentAdded("Wrong title"):
documentAdded("New Document"): expected: 1, actual: 0
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
at $Proxy0.documentAdded(Unknown Source)
at org.easymock.samples.ClassUnderTest.notifyListenersDocumentAdded(ClassUnderTest.java:61)
at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:28)
at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:30)
...
没有出现的所有预期记录都会表现出来。会把不是预期的调用做完全的比较(这个例子里没有出现)。如果方法调用次数过多,Mock对象也会抱怨:
java.lang.AssertionError:
Unexpected method call documentAdded("New Document"):
documentAdded("New Document"): expected: 1, actual: 1 (+1)
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
at $Proxy0.documentAdded(Unknown Source)
at org.easymock.samples.ClassUnderTest.notifyListenersDocumentAdded(ClassUnderTest.java:62)
at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:29)
at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:30)
...
1.4. 验证行为
这里有一个我们一直没有处理的问题:如果我们指定一个行为,我们应该验证它是否确实使用了。刚才的测试会在Mock对象没有调用的时候通过,为了严整指定的行为是否使用,我们需要调用verify(mock)。
public void testAddDocument() {
mock.documentAdded("New Document"); // 2
replay(mock); // 3
classUnderTest.addDocument("New Document", new byte[0]);
verify(mock);
}
如果Mock中的方法没有调用,我们会得到下面的异常:
java.lang.AssertionError:
Expectation failure on verify:
documentAdded("New Document"): expected: 1, actual: 0
at org.easymock.internal.MocksControl.verify(MocksControl.java:70)
at org.easymock.EasyMock.verify(EasyMock.java:536)
at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:31)
...
异常信息包括了所有没有运行的期望。
1.5. 期待对方法调用特定次数
到现在为止。我们的测试都只考虑单一方法调用。下一个测试需要测试,有一个已经存在的document添加的时候,是否会引起mock.documentChanged()方法,并使用适当的参数。为了确认。我们检测三次(只是作为例子而已:))。
public void testAddAndChangeDocument() {
mock.documentAdded("Document");
mock.documentChanged("Document");
mock.documentChanged("Document");
mock.documentChanged("Document");
mock.documentChanged("Document");
replay(mock);
classUnderTest.addDocument("Document", new byte[0]);
classUnderTest.addDocument("Document", new byte[0]);
classUnderTest.addDocument("Document", new byte[0]);
classUnderTest.addDocument("Document", new byte[0]);
verify(mock);
}
为了避免重复输入mock.documentChanged("Document"),EasyMock提供了一个捷径。我们在expectLastCall()后边使用times(int times)方法,指定调用的次数。代码如下:
public void testAddAndChangeDocument() {
mock.documentAdded("Document");
mock.documentChanged("Document");
expectLastCall().times(3);
replay(mock);
classUnderTest.addDocument("Document", new byte[0]);
classUnderTest.addDocument("Document", new byte[0]);
classUnderTest.addDocument("Document", new byte[0]);
classUnderTest.addDocument("Document", new byte[0]);
verify(mock);
}
如果方法调用了太多次,我们会获得一个异常,告诉我们方法调用了过多次。在第一个方法调用超过次数的时候,就会立即产生错误。
java.lang.AssertionError:
Unexpected method call documentChanged("Document"):
documentChanged("Document"): expected: 3, actual: 3 (+1)
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)
at $Proxy0.documentChanged(Unknown Source)
at org.easymock.samples.ClassUnderTest.notifyListenersDocumentChanged(ClassUnderTest.java:67)
at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:26)
at org.easymock.samples.ExampleTest.testAddAndChangeDocument(ExampleTest.java:43)
如果调用次数比预期少,verify(mock)就会抛出一个AssertionError:
java.lang.AssertionError:
Expectation failure on verify:
documentChanged("Document"): expected: 3, actual: 2
at org.easymock.internal.MocksControl.verify(MocksControl.java:70)
at org.easymock.EasyMock.verify(EasyMock.java:536)
at org.easymock.samples.ExampleTest.testAddAndChangeDocument(ExampleTest.java:43)
...
1.6. 指定返回值
为了指定返回值,我们在expect(T value)中封装了预期的调用,在它返回的对象中,使用andReturn(Object returnValue)指定了返回值。
作为例子,我们检测删除document的流程。如果ClassUnderTest得到了一个删除document的调用。它会询问所有的合作者的表决,来调用byte voteForRemoval(String title)来进行删除。正的返回值表示同意删除。如果所有值的和是正数,就删除document并调用所有合作者的documentRemoved(String title):
public void testVoteForRemoval() {
mock.documentAdded("Document"); // expect document addition
// expect to be asked to vote for document removal, and vote for it
expect(mock.voteForRemoval("Document")).addReturn((byte) 42);
mock.documentRemoved("Document"); // expect document removal
replay(mock);
classUnderTest.addDocument("Document", new byte[0]);
assertTrue(classUnderTest.removeDocument("Document"));
verify(mock);
}
public void testVoteAgainstRemoval() {
mock.documentAdded("Document"); // expect document addition
// expect to be asked to vote for document removal, and vote against it
expect(mock.voteForRemoval("Document")).andReturn((byte) -42);
replay(mock);
classUnderTest.addDocument("Document", new byte[0]);
assertFalse(classUnderTest.removeDocument("Document"));
verify(mock);
}
返回值的类型,会在编译阶段检查。作为例子,下边的代码无法通过编译,因为提供的返回值与方法的返回值不匹配:
expect(mock.voteForRemoval("Document")).andReturn("wrong type");
1.7. 不使用expect(T value)获得为对象设置的返回值,我们可以使用expectLastCall
EasyMock使用说明的更多相关文章
- Atitit.项目修改补丁打包工具 使用说明
Atitit.项目修改补丁打包工具 使用说明 1.1. 打包工具已经在群里面.打包工具.bat1 1.2. 使用方法:放在项目主目录下,执行即可1 1.3. 打包工具的原理以及要打包的项目列表1 1. ...
- awk使用说明
原文地址:http://www.cnblogs.com/verrion/p/awk_usage.html Awk使用说明 运维必须掌握的三剑客工具:grep(文件内容过滤器),sed(数据流处理器), ...
- 测试--easymock的使用
使用场景:对于调用其它类中的方法,但是还没有编写完,使用easymock进行单元测试,它提供这些没有编写完的代码期待的默认值. 使用步骤: step1: pom引入: <dependency&g ...
- “我爱背单词”beta版发布与使用说明
我爱背单词BETA版本发布 第二轮迭代终于画上圆满句号,我们的“我爱背单词”beta版本已经发布. Beta版本说明 项目名称 我爱背单词 版本 Beta版 团队名称 北京航空航天大学计算机学院 拒 ...
- Oracle 中 union 和union all 的简单使用说明
1.刚刚工作不久,经常接触oracle,但是对oracle很多东西都不是很熟.今天我们来了解一下union和union all的简单使用说明.Union(union all): 指令的目的是将两个 S ...
- Map工具系列-02-数据迁移工具使用说明
所有cs端工具集成了一个工具面板 -打开(IE) Map工具系列-01-Map代码生成工具说明 Map工具系列-02-数据迁移工具使用说明 Map工具系列-03-代码生成BySQl工具使用说明 Map ...
- Map工具系列-03-代码生成BySQl工具使用说明
所有cs端工具集成了一个工具面板 -打开(IE) Map工具系列-01-Map代码生成工具说明 Map工具系列-02-数据迁移工具使用说明 Map工具系列-03-代码生成BySQl工具使用说明 Map ...
- jQuery验证控件jquery.validate.js使用说明
官网地址:http://bassistance.de/jquery-plugins/jquery-plugin-validation jQuery plugin: Validation 使用说明 转载 ...
- gdbsever 使用说明
gdbsever 使用说明 在新塘N3292x平台下 编译 gdbsever ./configure --target=arm-linux --host=arm-linux arm-linux-gdb ...
随机推荐
- 初识jQuery 2013-09-26
常用选择器 $("#bad") id选择器 $("div#bad") id为bad 并且必须是div的元素 $("[href]") ...
- Oracle 性能相关常用脚本(SQL)
在缺乏的可视化工具来监控数据库性能的情形下,常用的脚本就派上用场了,下面提供几个关于Oracle性能相关的脚本供大家参考.以下脚本均在Oracle 10g测试通过,Oracle 11g可能要做相应调整 ...
- ios 页面传值4种方式(四) 之通过delegate(代理)
这是ios里最常用的设计模式了,简直贯穿了整个cocoa touch框架.废话不多说,直接上代码: 场景是: A--打开--B; B里输入数值,点击--返回--A; A里显示B输入的值; △在开始写之 ...
- hdu 1023(java实现进度计算)
题意:就是问你火车出战的方案数. 分析:卡特兰数的模板题,递推公式:a[n]=a[n-1]*(4*n-2)/(n+1). java代码实现: import java.util.*; import ja ...
- android操作SQLite
一.SQLite SQLite是一种转为嵌入式设备设计的轻型数据库,其只有五种数据类型,分别是: NULL: 空值 INTEGER: 整数 REAL: 浮点数 TEXT: 字符串 BLOB: 大数据 ...
- JavaScript专业规则12条
学习JavaScript是困难的.它发展的如此之快,以至于在任何一个特定的时刻,你都不清楚自己是否“做错了”.有些时候,感觉像是坏的部分超过了好的部分.然而,讨论这些并没有意义,JavaScript正 ...
- 从高铁G18中高端如厕看12306的验证码
1.引子 最近疯狂的吐槽12306网站的虐心验证码. 从对铁老大的一贯作风来说,这个事不过是芝麻绿豆的事情.这个事件只是因为发生在网络上,而引起了广大网民的一致谴责而已. 相信更丰富的如厕经历,大家只 ...
- 一个Web页面的生命周期 ,面试常常被问到
常规页生命周期阶段 一般来说,页要经历下表概述的各个阶段.除了页生命周期阶段以外,在请求前后还存在应用程序阶段,但是这些阶段并不特定于页.有关更多信息,请参见 ASP.NET 应用程序生命周期概述. ...
- Windows Server 2003单网卡搭建VPN
Windows Server 2003单网卡搭建VPN 1.打开[控制面板] --> [管理工具] --> [路由和远程访问] 2.鼠标右击你要管理的电脑 在弹出式菜单中选中[配置并启 ...
- 主机找不到vmnet1和vmnet8
今天跑程序时,突然发现虚拟机ping不通主机了,返过来可行,防火墙什么的都设置好了,仍然不行,后来发现,在网络和共享中心已经看不到vmnet1和vmnet8了,更改适配器设置也只有本地连接和宽带连接, ...