EasyMock入门使用
作者:互联网
EasyMock入门使用
一、EasyMock安装
-
EasyMock是一套通过简单的方法对于指定的接口或类生成Mock对象的类库,它能利用对接口或类的模拟来辅助单元测试。
-
要使用EasyMock辅助单元测试,添加easymock的jar包即可。
二、EasyMock使用
通过EasyMock,我们可以为指定的接口动态地创建Mock对象,并利用Mock对象来模拟协同模块或领域对象,从而使单元测试顺利进行。此过程可划分为以下步骤:
- 使用EasyMock生成Mock对象
- 设定Mock对象的预期行为和输出
- 将Mock对象切换到Replay状态
- 调用Mock对象方法进行单元测试
- 对Mock对象的行为进行验证
2.1 使用EasyMock生成Mock对象
根据指定的接口或类,EasyMock能动态地创建Mock对象。
2.1.1 为接口生成Mock对象
Eg:以ResultSet
接口为例
public interface java.sql.ResultSet {
......
public abstract java.lang.String getString(int arg0) throws java.sql.SQLException;
public abstract double getDouble(int arg0) throws java.sql.SQLException;
......
}
-
通常,构建一个真实的
RecordSet
对象需要经过一个复杂的过程:在开发过程中,开发人员通常会编写一个DBUtility
类来获取数据库连接Connection
,并利用Connection
创建一个Statement
。执行一个Statement
可以获取到一个或多个ResultSet
对象。这样的构造过程复杂并且依赖于数据库的正确运行。数据库或是数据库交互模块出现问题,都会影响单元测试的结果。 -
我们可以使用 EasyMock 动态构建
ResultSet
接口的 Mock 对象来解决这个问题。
static import org.easymock.Easymock; //静态方法引入
ResultSet mockResultSet = createMock(ResultSet.class);
或
import org.easymock.Easymock;
ResultSet mockResultSet = Easymock.createMock(ResultSet.class);
2.1.2 为类生成Mock对象
EasyMock默认只支持为接口生成Mock对象,若要为类生成Mock对象,需下载扩展包 EasyMock Class Extension
,在对具体类进行模拟时,只需把 org.easymock.EasyMock
替换为 org.easymock.classextension.Easymock
。
2.1.3 使用IMocksControl对象管理Mock对象
若在相对复杂的测试用例中使用多个Mock对象,可使用EasyMock提供的生成和管理Mock对象的机制。
EasyMock类的createControl
方法能创建一个接口IMocksControl
的对象,此对象能创建并管理多个Mock对象。
Eg:
IMocksControl control = EasyMock.createControl();
java.sql.Connection mockConnection = control.createMock(Connection.class);
java.sql.Statement mockStatement = control.createMock(Statement.class);
java.sql.ResultSet mockResultSet = control.createMock(ResultSet.class);
2.2 设定Mock对象的预期行为和输出
-
在一个完整测试中,一个Mock对象会经历两个状态:Record状态和Replay状态。
Mock对象一经创建,状态为Record,Record状态下用户可以设定Mock对象的预期行为和输出,这些对象行为会被录制下来,保存在Mock对象中。
-
添加Mock对象行为的过程分为三步:
- 对Mock对象的特定方法作出调用
- 通过org.easymock.EasyMock提供的静态方法expectLastCall获取上一次方法调用所对应的IExpectionSetters实例
- 用过IExpectionSetters实例设定Mock对象的预期输出(有两种类型)
- 产生返回值
- 抛出异常
(Mock对象的行为可简单理解为Mock对象的调用和方法调用所产生的输出。)
2.2.1 设定预期返回值
设定返回值对应接口IExpectionSetters
的andReturn
方法。
IExpectationSetters<T> andReturn(T value);
2.2.1.1 返回值不是void
-
Eg:仍用
ResultSet
接口的 Mock 对象为例,若希望方法mockResultSet.getString(1)
的返回值为 "My return value",则:mockResultSet.getString(1); EasyMock.expectLastCall().andReturn("My return value");
或:
EasyMock.expect(mockResultSet.getString(1)).andReturn("My return value");
-
若希望 某方法的调用总是返回一个相同的值 ,为避免每次调用都为Mock对象的行为进行一次设定,可用默认返回值的方法——
andSubReturn
方法。void andStubReturn(Object value);
Eg:假设我们创建了
Statement
和ResultSet
接口的 Mock 对象 mockStatement 和 mockResultSet,在测试过程中,我们希望 mockStatement 对象的executeQuery
方法总是返回 mockResultSet,则:mockStatement.executeQuery("SELECT * FROM sales_order_table"); EasyMock.expectLastCall().andStubReturn(mockResultSet);
或:
EasyMock.expect(mockStatement.executeQuery("SELECT * FROM sales_order_table")).andStubReturn(mockResultSet);
2.2.1.2 返回值为void
若方法的返回值类型为void,则对于此类方法,我们无需设定返回值,只需设置调用次数就可以。(也可以不设置)
How设定调用次数,详看2.2.3。
Eg:以 ResultSet
接口的 close
方法为例,假设在测试过程中,该方法被调用3至5次,则:
mockResultSet.close();
EasyMock.expectLastCall().times(3,5);// 最新版本的EasyMock可以忽略此句
或:
EasyMock.expect(mockResult.close()).times(3, 5);
2.2.2 设定预期异常抛出
设定预期抛出异常对应接口IExpectionSetters
的andThrow
方法。
IExpectationSetters<T> andThrow(Throwable throwable);
类似的,设定抛出默认异常对应接口IExpectionSetters
的andStubThrow
方法。
void andStubThrow(Throwable throwable);
Eg:
EasyMock.expectLastCall().andThrow(
new MyException(new RuntimeException())).anyTimes();
2.2.3 设定预期方法调用次数
- 设定确定的调用次数:通过接口
IExpectionSetters
的times
方法。
IExpectationSetters<T>times(int count);
Eg:我们希望 mockResultSet 的 getString
方法在测试过程中被调用3次,期间的返回值都是 "My return value",则:
mockResultSet.getString(1);
expectLastCall().andReturn("My return value").times(3);
或:
EasyMock.expect(mockResultSet.getString(1)).andReturn("My return value").times(3);
- 设定非准确调用次数:
times(int minTimes, int maxTimes)
:该方法最少被调用 minTimes 次,最多被调用 maxTimes 次。atLeastOnce()
:该方法至少被调用一次。anyTimes()
:该方法可以被调用任意次。
2.2.4 若返回结果在运行时才能确定
很可能某个方法期望的返回结果不是固定的,例如根据传入参数不同而不同;这时需要使用andAnswer()方法:
EasyMock.expect(mockService.execute(EasyMock.anyInt())).andAnswer(new IAnswer<Integer>() {
public Integer answer() throws Throwable {
Integer count = (Integer) EasyMock.getCurrentArguments()[0];
return count * 2;
}
});
{% note warning %}
注意:通过EasyMock.getCurrentArguments()
可以获取传入参数!
{% endnote %}
2.3 将Mock对象切换到Replay状态——replay
-
在生成Mock对象和设定Mock对象行为的两个阶段,Mock对象的状态均为Record,此阶段Mock对象会记录用户对预期行为和输出的设定。
-
在使用Mock对象隐形实际的测试前,需将Mock对象的状态切换为Replay,此阶段Mock对象能根据设定对特定的方法调用作出预期的响应。
-
将对象切换到Replay状态有两种方法:
-
若Mock对象是通过createMock方法生成
EasyMock.replay(mockResultSet);
-
若Mock对象通过createControl方法生成的接口对象control的createMock方法生成
control.replay();
此语句可将通过coltrol的createMock方法生成的所有Mock对象均切换为Replay状态。
-
2.4 调用Mock对象方法进行单元测试
此部分放到JUnit+EasyMock实例中。
2.5 对Mock对象的行为进行验证——verify
在利用Mock对象进行实际的测试过程后,还需对Mock对象的方法调用的次数进行验证。
-
若Mock对象是通过createMock方法生成
EasyMock.verify(mockResultSet);
-
若Mock对象通过createControl方法生成的接口对象control的createMock方法生成
control.verify();
同理,此语句可验证通过coltrol的createMock方法生成的所有Mock对象方法的调用次数。
2.6 Mock对象的重用——reset
为避免生成过多的Mock对象,EasyMock允许对原有的Mock对象进行重用。
可使用reset方法对Mock对象重新初始化。重新初始化后,Mock对象被置为Record状态。
-
若Mock对象是通过createMock方法生成
EasyMock.reset(mockResultSet);
-
若Mock对象通过createControl方法生成的接口对象control的createMock方法生成
control.reset();
同理,此语句可验证通过coltrol的createMock方法生成的所有Mock对象重新初始化。
三、在EasyMock中使用参数匹配器
使用Mock对象进行实际的测试过程中,EasyMock会根据方法名和参数来匹配一个预期方法的调用。
此时,EasyMock对参数的匹配默认使用equals()方法进行比较,这可能会引起一些问题,因此EasyMock提供了一些参数匹配方式。
{% note info %}
如2.2.1.1中创建的mockStatement对象:
mockStatement.executeQuery("SELECT * FROM sales_order_table");
EasyMock.expectLastCall().andStubReturn(mockResultSet);
实际调用中,可能会遇到SQL语句中某些关键词大小写问题(因为SQL语句不区分大小写)如将SELECT写成select,此时EasyMock采用的默认匹配器equals方法将认为参数不匹配,则Mock对象的预期方法不会被调用。
{% endnote %}
3.1 EasyMock预定义的参数匹配器
anyObject()
:任意输入值都与预期值匹配;aryEq(X value)
:通过Arrays.equals()
进行匹配,适用于数组对象;isNull()
:当输入值为Null时匹配;notNull()
:当输入值不为Null时匹配;same(X value)
:当输入值和预期值是同一个对象时匹配;lt(X value), leq(X value), geq(X value), gt(X value)
:当输入值小于、小等于、大等于、大于预期值时匹配,适用于数值类型;startsWith(String prefix), contains(String substring), endsWith(String suffix)
:当输入值以预期值开头、包含预期值、以预期值结尾时匹配,适用于String类型;matches(String regex)
:当输入值与正则表达式匹配时匹配,适用于String类型。
例如:若我对mockStatement具体执行的语句并不关注,希望所有输入的字符串都能够匹配这一方法的调用
EasyMock.expect(mockStatement.executeQuery(anyObject())).andStubReturn(mockResultSet);
详细参阅 easymock教程-参数匹配
3.2 EasyMock自定义参数匹配器
-
预定义的参数匹配器无法满足一些复杂情况,此时需自己定义参数匹配器。
如:在3.1中我们希望有一个匹配器对SQL中关键词的大小写不敏感,使用anyObject其实并不好,此时我们可以自定义参数匹配器SQLEquals。
-
How自定义参数匹配器:
-
实现
org.easymock.IArgumentMatcher
接口-
matches(Object actual)
方法应当实现输入值和预期值的匹配逻辑 -
appendTo(StringBuffer buffer)
方法可以添加当匹配失败时需要显示的信息
-
-
使用静态方法包装实现接口的类
package org.easymock.demo.matcher; import static org.easymock.EasyMock.reportMatcher; import org.easymock.IArgumentMatcher; //实现IArgumentMatcher接口 public class SQLEquals implements IArgumentMatcher { private String expectedSQL = null; public SQLEquals(String expectedSQL) { this.expectedSQL = expectedSQL; } //当匹配失败时需要显示的信息 public void appendTo(StringBuffer buffer) { buffer.append("SQLEquals(\"" + expectedSQL + "\")"); } //输入值和预期值的匹配逻辑 public boolean matches(Object actualSQL) { if (actualSQL == null && expectedSQL == null) return true; else if (actualSQL instanceof String) return expectedSQL.equalsIgnoreCase((String) actualSQL); else return false; } //自定义参数匹配器SQLEquals静态方法 public static String sqlEquals(String in) { reportMatcher(new SQLEquals(in)); return in; } }
使用自定义的sqlEquals匹配器:
EasyMock.expect(mockStatement.executeQuery(sqlEquals("SELECT * FROM sales_order_table"))).andStubReturn(mockResultSet);
-
四、特殊的Mock对象类型
上述创建的Mock对象都属于EasyMock默认的Mock对象类型,它对预期方法的调用顺序不敏感,对非预期的方法调用抛出AssertionError。
除此默认类型,EasyMock还提供一些特殊的Mock类型用于支持不同的需求。
4.1 Strick Mock对象
Stick Mock对象——对方法调用的先后顺序敏感,创建方法如下:
-
使用
EasyMock.createStrickMock()
来创建:ResultSet strickMockResultSet = EasyMock.createStrickMock(ResultSet.class);
-
类似于createMock,同样可用
IMocksControl
实例来创建一个Stick Mock对象:IMocksControl control = EasyMock.createStrictControl(); ResultSet strickMockResultSet = control.createMock(ResultSet.class);
4.2 Nice Mock对象
Nice Mock对象——默认返回0,null或false等“无效值”,创建方法如下:
-
使用
EasyMock.createNiceMock()
来创建:ResultSet strickMockResultSet = EasyMock.createNiceMock(ResultSet.class);
-
类似于createMock,同样可用
IMocksControl
实例来创建一个Nice Mock对象:IMocksControl control = EasyMock.createNiceControl(); ResultSet strickMockResultSet = control.createMock(ResultSet.class);
五、EasyMock 的工作原理
六、参考文献
标签:调用,匹配,入门,EasyMock,对象,使用,方法,Mock 来源: https://www.cnblogs.com/angelia-wang/p/16057558.html