美文网首页Android知识
从Java反射讲到Mockito

从Java反射讲到Mockito

作者: Lshare_Blog | 来源:发表于2017-05-10 18:50 被阅读217次

    Mockito是用于Java单元测试的一套流行框架,在使用它之前我们需要了解其背景知识。让我们立刻开始学习吧。

    Java反射

    Java的类型信息分为编译时类型信息和运行时类型信息,而反射就是Java提供的对运行时类型信息获取和操作的机制。通过反射我们可以获取到整个Class的结构,包含成员变量和成员方法,进行调用成员方法,修改其成员变量的值。注意,我们无法更改这些元素的结构。

    Constructor<MyClass> constructor = MyClass.class.getConstructor(int.class);
    MyClass myClass1 = constructor.newInstance(10);
    Method method = MyClass.class.getMethod("increase", int.class);
    method.invoke(myClass1, 4);
    Field field = MyClass.class.getField("count");
    System.out.println("Reflect-->" + field.getInt(myClass1));
    
    Java反射调用时序图

    Java代理与动态代理

    代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

    代理模式

    为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。
    Java提供的动态代理API:

    • Proxy:提供一组静态方法为一组接口动态地生成代理类和对象。
    • InvocationHandler:调用处理器,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
    • ClassLoader:类装载器,Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。
      运行下面代码将抛出异常,因为我们对List进行了代理,然后禁用了add方法。
    List list =
        (List) Proxy.newProxyInstance(List.class.getClassLoader(), new Class[] { List.class },
            new InvocationHandler() {
              @Override public Object invoke(Object proxy, Method method, Object[] args)
                  throws Throwable {
                if ("add".equals(method.getName())) {
                  throw new IllegalAccessException(method.getName() + " 方法被禁用了");
                }
                return method.invoke(proxy, args);
              }
            });
    list.add("sth");
    

    反模式

    反模式就是给出了问题的不好的解决方案。常见的反模式见维基百科

    Mock概念

    Mock就是对测试的类所依赖的其他类和对象进行仿制,即构建它们的一个假的对象,定义这些假对象上的行为,然后提供给被测试对象使用。被测试对象像使用真的对象一样使用它们。

    User mockUser = Mockito.mock(User.class);
    

    引入Mockito

    在你的gradle中加入下面的代码:

    repositories { 
      jcenter() 
    }
    dependencies { 
      testCompile "org.mockito:mockito-core:2.+"
     }
    

    android-support:

     repositories {
       jcenter()
     }
     dependencies {
       testCompile "org.mockito:mockito-core:+"
       androidTestCompile "org.mockito:mockito-android:+"
     }
    

    Mockito设计

    使用简单易读的代码进行TDD。功能上具有Mock对象、设定特定参数的方法调用时的响应、验证特定参数的方法调用的响应。基本流程是:先创建mock对象,然后设置mock对象上的方法;然后,调用被测试方法(被测试方法会调用mock对象的方法);最后进行验证。

    Mockito的设计是一种反模式,因为调用Mockito的方法会对有“副作用”--保存方法调用的信息,然后利用这些信息完成任务。正常的方法调用除了return值之外不会产生其它的状态改变。但是,Mockito的反模式使得其API的简洁、编译安全和易于重构。

    Mockito API

    • mock() / @Mock:创建mock对象
      • 可通过Answer/ReturnValues/MockSettings参数指定其行为
      • 通过when()/given()方法指定其行为
      • 如果提供的answers不符合你的要求,可以写一个Answer的实现
    • spy()/@Spy:可以对真实对象(而不是类)的方法进行mock,仍然可以verify和stubbed
    • verify():检查给定参数的方法是否被调用
      • 可以灵活地匹配参数,礼物通过any()匹配任意值
      • @Captor标注的地方捕获参数

    示例:

    import org.mockito.Mockito;
    
    // 创建mock对象
    List mockedList = Mockito.mock(List.class);
    
    // 设置mock对象的行为 - 当调用其get方法获取第0个元素时,返回"one"
    Mockito.when(mockedList.get(0)).thenReturn("one");
    
    // 使用mock对象 - 会返回前面设置好的值"one",即便列表实际上是空的
    String str = mockedList.get(0);
    
    Assert.assertTrue("one".equals(str));
    Assert.assertTrue(mockedList.size() == 0);
    
    // 验证mock对象的get方法被调用过,而且调用时传的参数是0
    Mockito.verify(mockedList).get(0); 
    

    更多使用方法查看:http://static.javadoc.io/org.mockito/mockito-core/2.7.22/org/mockito/Mockito.html

    参考

    相关文章

      网友评论

        本文标题:从Java反射讲到Mockito

        本文链接:https://www.haomeiwen.com/subject/lpvwuttx.html