它是開發模式: 測試驅動開發前端
它是工具:EasyMock, JMock, Mockito, Powermock-*java
EasyMock,JMock,Mockito: 對象模擬技術,只能模擬公共非靜態方法。
Powermock: PowerMock基於三者擴展,可以模擬靜態類、靜態方法、私有方法、構造方法等等。git
它強調的是業務邏輯的聯通性,通常用於單元測試和集成測試!github
No Dependency:每個團隊都但願本身開發的模塊不依賴任何其它的外界條件,溝通成本僅限於雙方接口定義。後端
敏捷、輕量級api
避免開發模塊之間的耦合框架
簡單 極爲靈活函數
經過定義基於方法的模擬調用規則模擬任何代碼的調用過程替代真實代碼執行!工具
模擬RPC服務:目前存在不少應用經過RPC服務調用獲取數據,應用前端的展示嚴重依賴後端服務的穩定性,在測試階段能夠選擇經過模擬的方式直接模擬後端服務。單元測試
Mockito+Powermock-*
相對於EasyMock和JMock,Mockito的書寫風格更爲簡潔。
模擬公共方法(public)
模擬私有方法(private)
模擬公共靜態方法(public static)
模擬私有靜態方法(private static)
模擬構造函數(public constructor)
模擬私有構造函數但存在公共建立實例的方法(private construtor)
模擬包含final修飾符的函數(非靜態函數同private, 靜態函數同private static)
模擬公共方法
業務代碼
UserAction: public void executeForPublic(String something){ userService.sayHi(something); System.out.println(userService.sayHello(something)); }
UserService: public void sayHi(String arg){ System.out.println("real"+arg+"!"); } public String sayHello(String arg){ return "real"+arg+"!"; }
測試樣例
``` java
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Test;
import org.mockito.Mockito;
import org.wit.service.UserAction;
import org.wit.service.UserService;
public class MockForPublicDemo {
@Test public void demo(){ UserService userService = mock(UserService.class); //userService.sayHi(any(String.class)); //對無返回值的方法 mock(UserService.class);後內部執行邏輯會被空調用覆蓋. Mockito.doNothing().when(userService).sayHi(any(String.class)); //有返回值的方法 when(userService.sayHello(any(String.class))).thenReturn("mock sayHello!"); // 設置業務服務. UserAction userAction = new UserAction(); userAction.setUserService(userService); // 執行目標業務方法. userAction.executeForPublic("public"); // 執行校驗. verify(userService, times(1)).sayHello(any(String.class)); verify(userService, times(1)).sayHi(any(String.class)); }
}
```
輸出:mock sayHello!
模擬私有方法
業務代碼
UserAction: public void executeForPrivate(String something){ userService.secreteSay(something); }
UserService: public void secreteSay(String arg){ secreteSayHi(arg); System.out.println(secreteSayHello(arg)); } private void secreteSayHi(String arg){ System.out.println("real"+arg+"!"); } private String secreteSayHello(String arg){ return "real"+arg+"!"; }
測試代碼
```java
import static org.mockito.Matchers.any;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.wit.service.UserAction;
import org.wit.service.UserService;
@RunWith(PowerMockRunner.class)
@PrepareForTest({UserService.class,UserAction.class})
public class MockForPrivateDemo {
@Test public void demo() throws Exception { UserService userService = PowerMockito.spy(new UserService()); // 模擬返回值私有方法. PowerMockito.doReturn("mock").when(userService, "secreteSayHello", any(String.class)); // 模擬私有空方法. PowerMockito.doNothing().when(userService, "secreteSayHi", any(String.class)); // 設置業務服務. UserAction userAction = new UserAction(); userAction.setUserService(userService); // 調用業務方法. userAction.executeForPrivate("private"); // 驗證. PowerMockito.verifyPrivate(userService, Mockito.times(1)).invoke("secreteSayHello", any(String.class)); PowerMockito.verifyPrivate(userService, Mockito.times(1)).invoke("secreteSayHi", any(String.class)); }
}
```
輸出:mock
模擬靜態公共方法
業務代碼
java UserAction: public void executeForPublicStatic(String something){ StaticUserService.sayHi(something); System.out.println(StaticUserService.sayHello(something)); }
```java
StaticUserService:
public static void sayHi(String arg){
System.out.println("real"+arg+"!");
}
public static String sayHello(String arg){
return "real"+arg+"!";
}
**測試代碼**
java
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.wit.service.StaticUserService;
import org.wit.service.UserAction;
@RunWith(PowerMockRunner.class)
@PrepareForTest({StaticUserService.class,UserAction.class})
public class MockForPublicStaticDemo {
@Test public void demo() throws Exception { //mock會模擬全部的方法. //PowerMockito.mock(StaticUserService.class); //spy只會模擬指定模擬行爲的方法. PowerMockito.spy(StaticUserService.class); //模擬返回值的public T static 方法. when(StaticUserService.sayHello(any(String.class))).thenReturn("mock"); //模擬無返回值的public void static 方法 PowerMockito.doNothing().when(StaticUserService.class, "sayHi", anyString()); // 業務方法調用. UserAction userAction = new UserAction(); userAction.executeForPublicStatic("public static"); // 驗證 sayHello. PowerMockito.verifyStatic(Mockito.times(1)); StaticUserService.sayHello(anyString()); // 驗證 sayHi. PowerMockito.verifyStatic(Mockito.times(1)); StaticUserService.sayHi(anyString()); }
}
```
輸出:mock
模擬靜態私有方法
業務代碼
UserAction: public void executeForPrivateStatic(String something){ StaticUserService.secreteSay(something); }
public static void secreteSay(String arg){ secreteSayHi(arg); System.out.println(secreteSayHello(arg)); } private static void secreteSayHi(String arg){ System.out.println("real"+arg+"!"); } private static String secreteSayHello(String arg){ return "real"+arg+"!"; }
測試代碼
import static org.mockito.Matchers.any; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.wit.service.StaticUserService; import org.wit.service.UserAction; @RunWith(PowerMockRunner.class) @PrepareForTest({StaticUserService.class}) public class MockForPrivateStaticDemo { @Test public void demo() throws Exception { PowerMockito.spy(StaticUserService.class); //模擬返回值私有方法. //PowerMockito.when(userService, "secreteSayHello", any(String.class)).thenReturn("mock"); PowerMockito.doReturn("mock").when(StaticUserService.class, "secreteSayHello", any(String.class)); //模擬私有空方法. PowerMockito.doNothing().when(StaticUserService.class, "secreteSayHi", any(String.class)); // 執行業務方法. UserAction userAction = new UserAction(); userAction.executeForPrivateStatic("real"); userAction.executeForPrivateStatic("real"); // 驗證私有方法. PowerMockito.verifyPrivate(StaticUserService.class,Mockito.times(2)).invoke("secreteSayHello", any(String.class)); PowerMockito.verifyPrivate(StaticUserService.class,Mockito.times(2)).invoke("secreteSayHi", any(String.class)); } }
輸出: mock
輸出: mock
模擬構造函數
業務代碼
java UserAction: public void executeForConstructor(String arg){ System.out.println(new ConstructorService(arg).doNoting()); }
```java
ConstructorService
public class ConstructorService {
private String tag; public ConstructorService(String tag){ this.tag = tag; } public String doNoting(){ return tag+" doNoting!"; }
}
```
測試代碼
``` java
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.times;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.wit.service.ConstructorService;
import org.wit.service.UserAction;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ConstructorService.class,UserAction.class})
public class MockForConstructorDemo {
@Test public void testMockForConstructor() throws Exception { UserAction userAction = new UserAction(); //模擬構造函數. //value必須提早構造. ConstructorService value = new ConstructorService("mock"); PowerMockito.spy(ConstructorService.class); //模擬構造函數. PowerMockito.whenNew(ConstructorService.class).withArguments(anyString()).thenReturn(value); //執行業務邏輯. userAction.executeForConstructor("real"); //驗證構造方法. PowerMockito.verifyNew(ConstructorService.class, times(1)).withArguments(anyString()); }
}
```
輸出:mock doNoting!
模擬私有構造函數
業務代碼
UserAction: public void executeForPrivateConstrutor(String arg){ System.out.println(PrivateConstructorService.createInstance().getDetail(arg)); }
PrivateConstructorService: public class PrivateConstructorService { private PrivateConstructorService(){ } public String getDetail(String arg){ return "private service " + arg; } public static PrivateConstructorService createInstance(){ return new PrivateConstructorService(); } }
測試代碼
import static org.mockito.Mockito.when; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.wit.service.PrivateConstructorService; import org.wit.service.UserAction; @RunWith(PowerMockRunner.class) @PrepareForTest({PrivateConstructorService.class,UserAction.class}) public class MockForPrivateConstrutorDemo { @Test public void demo() throws Exception { PowerMockito.spy(PrivateConstructorService.class); // 使用PowerMockito建立擁有私有構造函數類的實例 PrivateConstructorService instance = PowerMockito.constructor(PrivateConstructorService.class).newInstance(new Object[]{}); // 模擬靜態函數. when(PrivateConstructorService.createInstance()).thenReturn(instance); // 業務方法調用. UserAction userAction = new UserAction(); userAction.executeForPrivateConstrutor("real"); // 驗證 sayHello. PowerMockito.verifyStatic(Mockito.times(1)); PrivateConstructorService.createInstance(); } }
輸出:private service real
Mock是CI利器,可以在測試階段最大程度減小各開發團隊之間的耦合,但它並不是萬能,畢竟實際上線時業務模塊之間必然是真實調用,因此它並不能替代聯調測試!