在easymock的使用過程中,當創建mock對象時,我們會遇到 strict mock和nice mock的概念。
比如創建mock對象我們通常使用EasyMock.createMock(),但是我們會發現easymock同時提供了兩個類似的方法:
EasyMock.createNiceMock()
EasyMock.createStrictMock()
類似的在創建MocksControl時,除了通常的EasyMock.createControl() 外,easymock也同時提供兩個類似的方法:
EasyMock.createNiceControl()
EasyMock.createStrictControl()
我們來看看strict和nice有什么作用。參考easymock的javadoc,我們對比createMock()和createStrictMock():
EasyMock.createMock(): Creates a mock object that implements the given interface, order checking is disabled by default.
EasyMock.createNiceMock() : Creates a mock object that implements the given interface, order checking is enabled by default.
發現strict mock方式下默認是開啟調用順序檢測的,而普通的mock方式則默認不開啟調用順序檢測。
再看一下createNiceMock():
Creates a mock object that implements the given interface, order checking is disabled by default, and the mock object will return 0, null or false for unexpected invocations.
和createMock()相同的是默認不開啟調用順序檢測,另外有一個非常有用的功能就是對于意料之外的調用將返回0,null 或者false.之所以說有用,是因為在我們的實際開發過程中,有時候會有這樣的需求:對于某個mock對象的調用(可以是部分,也可以是全部),我們完全不介意調用細節,包括是否調用和調用順序,參數,返回值,我們只要求mock對象容許程序可以繼續而不是拋出異常報告說 unexpected invocations 。nice mock在這種情況下可以為我們節省大量的工作量,非常方便。
我們來看一個簡單的實際使用的例子,假設我們有一個Business類,依賴于兩個service 接口:
先看只調用一個依賴的情況,注意在record階段service1.method2()和service1.method1()的順序和business.executeService1()方法中的實際調用順序是故意設置為不同的。

public class Business {
private Service1 service1;

private Service2 service2;

public void executeService1() {
service1.method1();
service1.method2();
}

public void executeService1And2() {
service1.method1();
service1.method2();
service2.method3();
service2.method4();
}


public void setService1(Service1 service1) {
this.service1 = service1;
}


public void setService2(Service2 service2) {
this.service2 = service2;
}
}


private interface Service1 {

public void method1();

public void method2();
}


private interface Service2 {

public void method3();

public void method4();
}
1. 普通mock
@Test

public void testMock() {
Business business = new Business();
Service1 service1 = EasyMock.createMock("service1", Service1.class);
business.setService1(service1);
service1.method2();
EasyMock.expectLastCall();
service1.method1();
EasyMock.expectLastCall();

EasyMock.replay(service1);
business.executeService1();
EasyMock.verify(service1);
}

測試案例可以通過,說明EasyMock.createMock()的確是不檢測方法的調用順序。
2. strict mock
@Test

public void testStrictMock() {
Business business = new Business();
Service1 service1 = EasyMock.createStrictMock("service1", Service1.class);
...
}


案例失敗,錯誤信息如下
java.lang.AssertionError:
Unexpected method call service1.method1():
service1.method2(): expected: 1, actual: 0
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:45)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:73)
at net.sourcesky.study.easymock.tutorial.$Proxy4.method1(Unknown Source)
at net.sourcesky.study.easymock.tutorial.OrderTest$Business.executeService1(OrderTest.java:14)
at net.sourcesky.study.easymock.tutorial.OrderTest.testStrictMock(OrderTest.java:79)
......
說明strict mock下,easymock檢測到了實際調用時的順序和預期的不同。
3. nick mock
@Test

public void testNiceMock() {
Business business = new Business();
Service1 service1 = EasyMock.createNiceMock("service1", Service1.class);
...
}
測試案例可以通過,而且如果是nick mock的話,record階段可以簡化:
@Test

public void testNiceMockSimplify() {
Business business = new Business();
Service1 service1 = EasyMock.createNiceMock("service1", Service1.class);
business.setService1(service1);
EasyMock.replay(service1);
business.executeService1();
EasyMock.verify(service1);
}
這個簡化版本的測試案例也是可以通過的。
上述的測試案例驗證了strict mock和nice mock的基本使用,對于同一個mock對象,strict模式下多個方法之間的調用順序在record階段和replay階段下是需要保持一致的。但是故事并不是到此結束,更有意思的內容在后面:如果出現多個mock對象,那么這些不同mock對象的方法之間,他們的調用順序是否檢測?普通mock和nice mock模式下自然是不會檢測順序,但是strict模式下呢?
我們來看需要測試的方法executeService1And2(),這個方法會依次調用service1和service2的方法。使用easymock測試這個方法,注意我們在record階段依然故意將方法的調用順序設置為和實際不同。
1. 不使用control,直接創建兩個strict mock對象
@Test

public void testWithoutControlInWrongOrder() {
Business business = new Business();
Service1 service1 = EasyMock.createStrictMock("service1", Service1.class);
Service2 service2 = EasyMock.createStrictMock("service2", Service2.class);
business.setService1(service1);
business.setService2(service2);
service2.method3();
EasyMock.expectLastCall();
service1.method1();
EasyMock.expectLastCall();
EasyMock.replay(service1, service2);
business.executeService1And2();
EasyMock.verify(service1, service2);
}
這個測試案例,出于意外的,通過了。easymock并沒有檢測service1.method1()和service2.method3()這兩個方法的調用順序。
2. 使用strict control創建兩個strict mock對象
@Test

public void testWithStrictControlInWrongOrder() {
Business business = new Business();
IMocksControl mocksControl = EasyMock.createStrictControl();
...
}
案例失敗,錯誤信息為:
java.lang.AssertionError:
Unexpected method call service1.method1():
service2.method3(): expected: 1, actual: 0
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:45)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:73)
at net.sourcesky.study.easymock.tutorial.$Proxy4.method1(Unknown Source)
at net.sourcesky.study.easymock.tutorial.OrderTest$Business.executeService1And2(OrderTest.java:19)
at net.sourcesky.study.easymock.tutorial.OrderTest.testWithStrictControlInWrongOrder(OrderTest.java:218)
......
OK,easymock終于檢測到service1.method1()和service2.method3()這兩個方法的調用順序和期望的不一致了。
解釋一下,EasyMock.createStrictMock()方法實際上內部是生成一個新的strict control,然后再創建mock對象。
Service1 service1 = EasyMock.createStrictMock("service1", Service1.class);
Service2 service2 = EasyMock.createStrictMock("service2", Service2.class);
這里實際是創建了兩個strict control,而easymock是不會跨control進行順序檢測的。在實際使用過程中,我們會有大量的場景需要檢測多個mock之間的調用順序(按說如果沒有特殊要求,一般的測試場景默認都應該如此),這種情況下就必須使用control, 而且必須是同一個strict control才能滿足要求。
教程后面的最佳實踐中有一條就是推薦使用mock control,可以跨mock對象檢測方法調用順序是一個重要原因。