Mock Final方法和类

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mock Final方法和类相关的知识,希望对你有一定的参考价值。

参考技术A 有时候写了一个final的类,但是又需要进行单元测试。直接使用Mockito一直报错,要怎么做呢?

在Mockito 2.1.0的时候,提供了Mock final的类和方法,在2.1.0的版本时,Mock Final是可选的特性,需要进行配置。有两种方式:

方式一:

1 在test的resources目录下,创建mockito-extensions目录

2 在mockito-extensions目录下,创建org.mockito.plugins.MockMaker文件。

3 在org.mockito.plugins.MockMaker文件中加上一行 mock-maker-inline

方式二:

直接引入mockito-inline的maven依赖

1 新建一个final的类和方法

2 使用Mockito进行Mock

3 查看效果

4 配置支持Final的类和方法

5 再次查看效果

对于Final的类,Mockito只能操作JDK9以下的版本,如果想要支持Java 9以上的版本,还需要手动配置

Byte Buddy Java agent jar 。使用 -javaagent

关于如何模拟Final的Class和method实现细节在 InlineByteBuddyMockMaker 类中,感兴趣的可以看一下。

主要说明了如何使用Mockito Mock Final的类和方法。

参考:

当final作用于变量参数方法和类时该如何处理

final变量:

  对于基本类型使用final:它就是一个常量,数值恒定不变

 

  对于对象引用使用final:使得引用恒定不变,一旦引用被初始化指向一个对象,就无法再把 它改为指向另一个对象。然而,对象自身却是可以被修改的,java并没有提供使任何对象恒定不变的途径。这一限制同样也使用数组,它也是对象。

例子:

 

class Value{
    int i;
    public Value(int i){
        this.i = i;
    }
}

public class FinalData {
    private static Random random = new Random(47);
    private String id;
    
    public FinalData(String id){
        this.id = id;
    }
    
    private final int valueOne = 9;
    private static final int VALUE_TWO = 99;
    public static final int VALUE_THREE = 39;
    private final int i4 = random.nextInt(20);
    static final int INT_5 = random.nextInt(20);
    
    private Value v1 = new Value(11);
    private final Value v2 = new Value(22);
    private static final Value VAL_3 = new Value(33);
    
    private final int[] a = {1, 2, 3, 4, 5, 6};
    public String toString(){
        return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5;
    }
    
        public static void main(String[] args) {
        FinalData fd1 = new FinalData("fd1");
        //! fd1.valueOne++; // 因为valueOne是基本类型常量,其数值恒定不变
        fd1.v2.i++; //final修饰的对象的内容可以改变
        fd1.v1 = new Value(9);
        for(int i = 0; i < fd1.a.length; i++)
            fd1.a[i]++;
        //! fd1.v2 = new Value(0); // 因为v2是final修饰的引用类型,其引用不能被修改指向另一个对象
        //! fd1.VAL_3 = new Value(1); // 表示占据一段不能改变的内存空间
        //! fd1.a = new int[3]; // final修饰的数组
        System.out.println(fd1);
        System.out.println("Creating new FinalData");
        FinalData fd2 = new FinalData("fd2");
        System.out.println(fd1);
        System.out.println(fd2);
    }
}
/*output:
fd1: i4 = 15, INT_5 = 18
Creating new FinalData
fd1: i4 = 15, INT_5 = 18
fd2: i4 = 13, INT_5 = 18
*/    

 

分析:

  对于fd1fd2两个对象,其中i4是唯一的,即每个对象都有一个i4,但INT_5被声明为static,即是类共享的,fd1fd2共享INT_5,在装载时已经被初始化,而不是每次创建新对象时初始化(例如i4);但它同时被设置成final,所以它的引用是不可改变的,即不能被修改指向另一个对象。

 

 

空白final

 

  被声明为final但又没有给定初值。必须在域的定义或者每个构造器中使用表达式对final进行赋值,这正是final域在使用前总是初始化的原因。

 

 

 

 

final参数:

  这意味着你无法在方法中更改参数引用,使其指向另一个参数,但可以修改final对象所指向的内容

例子:

class Gizmo{
    int i = 0;
    public void spin(){}
}

public class FinalArguments {
    void with(final Gizmo g){
        //! g = new Gizmo(); // 无法修改final修饰的引用,使它指向另一个对象
        g.i++;    // 但可以修改final对象所指向的内容
    }
    void without(Gizmo g){
        g = new Gizmo();
        g.spin();
    }
    
//    int g(final int i){
//        //! i++; //因为参数i是常量值
//    }
    
    int g(final int i){
        return i + 1;
    }
    
    public static void main(String[] args) {
        FinalArguments bf = new FinalArguments();
        bf.without(null);
        bf.with(null);
    }
}

分析:

  参数被声明为final,若是基本参数,那它就是一个常量,不能被修改;若是一个引用变量,那么它就不能被修改指向另一个对象,但可以修改该引用所指对象的内容。

 

fianl方法:

使用原因:

  1. 把方法锁定,以防任何继承类修改它的含义,即该方法不会被继承的类覆盖
  2. 效率,若一个方法指明为final,那么就同意编译器将针对该方法的所有调用转为内嵌调用。

类中所有的private方法都隐式地指定为final,由于无法取用private方法,所以也就无法覆盖它。可以对private方法添加final修饰词,但这并不会给该方法带来任何额外的意义。

例子:

class WithFinals{
    private final void f(){
        System.out.println("WithFinals.f()");
    }
    private void g(){
        System.out.println("OverridingPrivate.f()");
    }
}

class OverridingPrivate extends WithFinals{
    private final void f(){
        System.out.println("OverridingPrivate.f()");
    }
    private void g(){
        System.out.println("OverridingPrivate.g()");
    }
}

class OverridingPrivate2 extends OverridingPrivate{
    /*
     * 当使用Override注解强制使f()方法覆盖父类的f()方法时,会报错
     * 因为它不知道父类是否有该方法,对于g()方法来说,它只是生成了一个新的方法,
     * 并没有覆盖掉父类中的g()方法。
     */
    //@Override
    public final void f(){
        System.out.println("OverridingPrivate2.f()");
    }
    public void g(){
        System.out.println("OverridingPrivate2.g()");
    }
}

public class FinalOverridingIllusion{
    public static void main(String[] args) {
        OverridingPrivate2 op2 = new OverridingPrivate2();
        op2.f();
        op2.g();
        
        // 可以向上转型
        OverridingPrivate op = op2;
        //! op.f(); // 父类中final方法对子类来说是不可见的
        //! op.g();
        WithFinals wf = op2;
        // wf.f();
        // wf.g();
    }
}
/*output:
OverridingPrivate2.f()
OverridingPrivate2.g()
*/

分析:

  覆盖何时发生:

    1,子类中出现与父类完全一致的方法

    2. 子类可以通过向上转型为父类,并调用父类中的那个方法

  若父类中某个方法被声明为final或者private,那么这个方法对子类来说是不可见的,就算在子类中创建了与父类一模一样的方法,这也是一个新的方法,而不是从父类中覆盖的方法。

 

final类:

  即该类不能被继承,不管是你还是别人,也就是这个类不需要做任何变动,也不需要任何子类,例如String类。

例子:

 

class SmallBrain{}

final class Dinosaur{
    int i = 7;
    int j = 1;
    SmallBrain x = new SmallBrain();
    void f(){}
}
// error: The type Further cannot subclass the final class Dinosaur
// Dinosaur类不能有子类
// class Further extends Dinosaur{}

public class Jurassic {
    public static void main(String[] args) {
        Dinosaur n = new Dinosaur();
        n.f();
        n.i = 40;
        n.j++;
    }
}

 

以上是关于Mock Final方法和类的主要内容,如果未能解决你的问题,请参考以下文章

无所不能的PowerMock,mock私有方法,静态方法,测试私有方法,final类

PowerMock - 无所不能的PowerMock,mock私有方法,静态方法,测试私有方法,final类

PowerMock - 无所不能的PowerMock,mock私有方法,静态方法,测试私有方法,final类

Final关键字和类的自动加载

PowerMock使用指南

单元测试如何Mock final类