Mock
Mock(模拟测试)
What(它是什么?)##
它是开发模式: 测试驱动开发
它是工具:EasyMock, JMock, Mockito, Powermock-*
EasyMock,JMock,Mockito: 对象模拟技术,只能模拟公共非静态方法。
Powermock: PowerMock基于三者扩展,能够模拟静态类、静态方法、私有方法、构造方法等等。
它强调的是业务逻辑的联通性,一般用于单元测试和集成测试!
Requirements(需求)##
No Dependency:每一个团队都希望自己开发的模块不依赖任何其它的外界条件,沟通成本仅限于双方接口定义。
Why(为什么要使用它?)##
敏捷、轻量级
避免开发模块之间的耦合
简单 极为灵活
Principle##
通过定义基于方法的模拟调用规则模拟任何代码的调用过程替代真实代码执行!
How(如何使用?)##
场景###
模拟RPC服务:目前存在很多应用通过RPC服务调用获取数据,应用前端的展现严重依赖后端服务的稳定性,在测试阶段可以选择通过模拟的方式直接模拟后端服务。
选择哪一种模拟框架?###
Mockito+Powermock-*
why?###
相对于EasyMock和JMock,Mockito的书写风格更为简洁。
核心Api###
- org.mockito.Mockito.mock: 根据给定的类或实例创建模拟对象实例.
- org.mockito.Mockito.when: 绑定模拟行为.
- org.mockito.Mockito.verify: 用于校验模拟行为的预期结果,比如调用次数与返回值.
- org.mockito.Matchers.any: 用于生成任意类型的对象,例如any(String.class).
- org.mockito.Mockito.times: 用于校验模拟方法被调用次数的匹配.
- org.junit.runner.RunWith: Junit4之后开放给开发者自定义测试类运行器的注解.
- org.powermock.core.classloader.annotations.PrepareForTest(注解): 包裹上下文中的被模拟类和模拟类方法的调用者,提供一种标识给Powermock框架对其做字节码修改等处理.
- org.powermock.core.classloader.annotations.PowerMockIgnore(注解): 用于过滤由部分自定义类加载器或spi接口的类型的加载.
- org.powermock.api.mockito.PowerMockito.verifyStatic: 用于静态方法校验.
- org.powermock.api.mockito.PowerMockito.verifyPrivate: 用于私有方法校验.
入门示例###
模拟公共方法(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+"!"; }
测试样例
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+"!"; }
测试代码
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
-
模拟静态公共方法
业务代码
UserAction: public void executeForPublicStatic(String something){ StaticUserService.sayHi(something); System.out.println(StaticUserService.sayHello(something)); }
StaticUserService: public static void sayHi(String arg){ System.out.println("real"+arg+"!"); } public static String sayHello(String arg){ return "real"+arg+"!"; }
测试代码
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
-
模拟构造函数
业务代码
UserAction: public void executeForConstructor(String arg){ System.out.println(new ConstructorService(arg).doNoting()); }
ConstructorService public class ConstructorService { private String tag; public ConstructorService(String tag){ this.tag = tag; } public String doNoting(){ return tag+" doNoting!"; } }
测试代码
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流程##
- 初始化
- 定制规则
- 业务调用
- 验证
结束语##
Mock是CI利器,能够在测试阶段最大程度减少各开发团队之间的耦合,但它并非万能,毕竟实际上线时业务模块之间必然是真实调用,所以它并不能替代联调测试!