Mock

Mock(模拟测试)


What(它是什么?)

它是开发模式: 测试驱动开发前端

它是工具:EasyMock, JMock, Mockito, Powermock-*java

EasyMockJMockMockito: 对象模拟技术,只能模拟公共非静态方法。
Powermock: PowerMock基于三者扩展,可以模拟静态类、静态方法、私有方法、构造方法等等。git

它强调的是业务逻辑的联通性,通常用于单元测试和集成测试!github

Requirements(需求)

No Dependency:每个团队都但愿本身开发的模块不依赖任何其它的外界条件,沟通成本仅限于双方接口定义。后端

Why(为何要使用它?)

敏捷、轻量级api

避免开发模块之间的耦合框架

简单 极为灵活函数

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+"!";
    }

    测试样例
    ``` 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流程

  • 初始化
  • 定制规则
  • 业务调用
  • 验证

结束语

Mock是CI利器,可以在测试阶段最大程度减小各开发团队之间的耦合,但它并不是万能,毕竟实际上线时业务模块之间必然是真实调用,因此它并不能替代联调测试!

相关文章
相关标签/搜索