http://blog.youkuaiyun.com/dananhai381/article/details/8014044
特点
修改函数名称和参数顺序,不会影响重构
支持返回 value和异常
支持检察方法的调用顺序
安装
Maven方式
- <dependency>
- <groupId>org.easymock</groupId>
- <artifactId>easymock</artifactId>
- <version>3.1</version>
- <scope>test</scope>
- </dependency>
手动需要
Easymock cglib (2.2) and Objenesis (1.2)
使用实例
1. 在测试类中,静态引入org.easymock.EasyMock
的方法。
2. 从需要模拟的接口创建Mock 对象,并建立关系
- public void setUp() {
- mock = createMock(Collaborator.class); // 1
- classUnderTest = new ClassUnderTest();
- classUnderTest.addListener(mock);
- }
3. 记录期待的行为(mock哪个函数调用等)
4. Replay(mock) 支持多个mock作为参数
5. 对需要的类进行测试
6. verify(mock) 支持多个mock作为参数
Mock对象
类型
支持 StrictMock,NiceMock和Mock Object 三种方式。
StrictMock:检查函数调用次序,创建方法EasyMock.createStrictMock()
。也可以使用
switch order checking on by calling checkOrder(mock, true)
and switch it off by calling checkOrder(mock, false)
。
NiceMock:使用 createMock 创建出来的 mock 对象,如果没有录过,调用这个方法都会出 AssertionError ,但如果使用 createNiceMock 就不会了,会返回 0 , null , false 这样的。
普通的mock 为 createMock.
重置
Reset(Mock)
带名称的mock
CreateMock(String name,Class<T> toMock)
createStrictMock(String name, Class<T> toMock)
createNiceMock(String name, Class<T> toMock)
部分mock
对类的部分函数进行mock,当测试一个函数,该函数调用同类中的其他函数时,希望这个函数不被mock而mock其他函数。
处理方法:
1. 考虑重构,这是一种bad design
2. 使用方法
- ToMock mock = createMockBuilder(ToMock.class)
- .addMockedMethod("mockedMethod").createMock();
- 只有addMockedMethod添加的方法会mock,但抽象方法默认mock
object method
对象的四个方法 equals() hashCode() toString() finalize() 不能被修改
序列化
Mock对象支持序列化,使用writeObject
多线程
在record 阶段,mock不是线程安全的,不同的mock可以在不同的线程中同步录制
在replay阶段,mock是线程安全的,可通过 makeThredSafe修改状态
checkIsUsedInOneThred 判断是否在一个线程中调用
函数
希望的行为
l 对mock对象调用的次数限制:
expectLastCall().times(int times)
最后一个方法调用的次数。
l
指定返回值:
封装expect(mock调用函数返回值).andReturn()
expect(mock.voteForRemoval("Document")).andReturn((byte) 42);
l
处理例外
expectLastCall().andThrow(Throwable throwable)
expect(mock.fun()).andThrow(Throwable throwable)
对于要指定返回值,异常,次数的mock期望,在该函数后使用,可在程序中使用多次。
相同函数返回不同值
返回42 三次,扔出runtimeException 四次,返回-42一次
- expect(mock.voteForRemoval("Document"))
- .andReturn((byte) 42).times(3)
- .andThrow(new RuntimeException(), 4)
- .andReturn((byte) -42);
- // 当参数不等于42时,总是返回-1
- expect(mock.voteForRemoval(not(eq("Document")))).andStubReturn(-1);
创建return values或者exception
根据输入参数,动态创建return value,重构可能会破坏测试,如下
- expect(l.remove(10)).andAnswer(new IAnswer<String>() {
- public String answer() throws Throwable {
- return getCurrentArguments()[0].toString();
- }
- });
- //通过使用 andAnswer(IAnswer answer) 函数,在IAnswer的接口类中做具体处理
调用次数
times(count) times(min,max) atLeastOnce() anytime() once()
函数参数
使用matcher,如:
expect(mock.voteForRemovals(aryEq(documents))).andReturn(42);
支持的matcher有:
Eq(X value) | 支持object和primitive类型 |
anyBoolean(), anyByte(), anyChar(), anyDouble(), anyFloat(), anyInt(),anyLong(), anyObject(), anyObject(Class clazz), anyShort() | 匹配任意值 |
eq(X value, X delta) | 在delta偏差内,为float和double |
aryEq(X value) |
|
isNull(), isNull(Class clazz) |
|
notNull(), notNull(Class clazz) |
|
same(X value) |
|
isA(Class clazz) |
|
lt(X value), leq(X value), geq(X value), gt(X value) |
|
startsWith(String prefix), contains(String substring), endsWith(String suffix) |
|
matches(String regex), find(String regex) |
|
and(X first, X second) |
|
not(X value) |
|
or(X first, X second) |
|
cmpEq(X value) |
|
cmp(X value, Comparator<X> comparator, LogicalOperator operator) | the operator is <,<=,>,>= or ==. Available for objects |
capture(Capture<T> capture), captureXXX(Capture<T> capture) |
|
自定义Matcher
1. 实现接口org.easymock.IArgumentMatcher
.
2. 声明Matcher
构造函数mock
用在测试类方法,但其他method是mocked
ToMock mock = createMockBuilder(ToMock.class).withConstructor(1, 2, 3); // 1, 2, 3 are the constructor parameters
其他
类mock的限制
对equals() hasCode() finalize提供内置的支持
Final method,private method 不能mock,如果调用将执行normal code
修改默认行为
配置easymock.properties 修改相应属性
多mock之间的调用顺序
使用IMocksControl
EasyMockSupport
是工具类,自动注册所有的mock;批量replay、reset、校验。
需要继承EasyMockSupport,调用其定义的函数,如replayAll() verifyAll()