美文网首页
JMockit单元测试用法

JMockit单元测试用法

作者: visionarywind | 来源:发表于2018-06-02 16:21 被阅读525次

    基础概念

    • what
      JMockit是一款Java类/接口/对象的Mock工具,目前广泛应用于Java应用程序的单元测试中
    • why
      这差不多是目前最强大、最方便和最简单的测试框架了
    • Record-Replay-Verification单元测试结构

    Record: 即先录制某类/对象的某个方法调用,在当输入什么时,返回什么
    Replay: 即重放测试逻辑
    Verification: 重放后的验证

    • API
      @Mocked、@Tested、@Injectable、@Capturing、@Mock、Expectations、MockUp、Verifications

    API用法

    @Mocked

    加上了JMockit的API @Mocked, JMockit会实例化这个对象
    Mocked可以用来修饰类、接口和抽象类,返回默认值(如果是原始类型,返回原始值的默认值,如果为其他对象,则返回一个同样被Mocked的对象)
    Mocked十分粗暴,会mock掉整个的实现

    @Injectable

    @Injectable功能跟@Mocked相似,区别是@Injectable只针对修饰的实例,对mock类的静态方法、构造函数没有影响

    @Tested

    @Tested表示被测试对象。如果该对象没有赋值,JMockit会去实例化它,若@Tested的构造函数有参数,则JMockit通过在测试属性&测试参数中查找@Injectable修饰的Mocked对象注入@Tested对象的构造函数来实例化,
    不然,则用无参构造函数来实例化。除了构造函数的注入,JMockit还会通过属性查找的方式,把@Injectable对象注入到@Tested对象中。
    注入的匹配规则:先类型,再名称(构造函数参数名,类的属性名)。若找到多个可以注入的@Injectable,则选择最优先定义的@Injectable对象

    @Capturing

    @Capturing主要用于子类/实现类的Mock,比如java动态代理生成的类,这些类没有名字,无法进行正常的mock
    当只知道父类或接口时,但需要控制其子类的行为时,子类可能有多个实现(可能有人工写的,也可能是AOP代理自动生成时),需要使用@Capturing

    Expectations

    Record对象的核心方法,最核心和重要的注解了,可以和引用外部类的Mock对象(@Injectabe,@Mocked,@Capturing)来录制,也可以通过构建函数注入类/对象来录制
    限制:不能mock对象的native方法和private方法

    MockUp & Mock

    MockUp & @Mock提供更改代码行为的Mock方式,最强大mock实现,主要有以下限制:

    • 无法mock单个实例
    • 无法mock动态代理对象
    • 对类的所有方法都进行Mock,书写MockUp的代码量太大
      可以用于项目中比较通用模块,减少大量重复的Exceptations

    Verifications

    Verifications是用于做验证,过程式编程的福音
    验证Mock对象(即@Moked/@Injectable@Capturing修饰的或传入Expectation构造函数的对象)有没有调用过某方法,调用了多少次
    主要用于一些没有返回值的代码验证

    实例

    //一个普通类 
    public class AnOrdinaryClass {
        // 静态方法
        public static int staticMethod() {
            return 1;
        }
        // 普通方法
        public int ordinaryMethod() {
            return 2;
        }
        // final方法
        public final int finalMethod() {
            return 3;
        }
        // native方法,返回4
        public native int navtiveMethod();
        // private方法
        private int privateMethod() {
            return 5;
        }
        // 调用private方法
        public int callPrivateMethod() {
            return privateMethod();
        }
    }
    
    public class Sample {
        private SampleDepend sampleDepend;
        private SampleInterface sampleInterface = new SampleInterface() {
            @Override
            public int call() {
                return 1;
            }
        };
    
        public Sample() {
        }
    
        public int getSampleDependVal() {
            return sampleDepend.getVal();
        }
    
        private static int staticGetOne() { return 1; }
        public static int staticGetPrivateOne() { return staticGetOne(); }
    
        private int getOne() { return 1; }
        public int getPrivateOne() { return getOne(); }
    
        public void execute() {
            sampleDepend.getVal();
            doExecute(); }
    
        private void doExecute() {}
    
        public int interfaceCall() {
            return sampleInterface.call();
        }
    
        public interface SampleInterface {
            int call();
        }
    }
    public class SampleDepend {
        private int val;
        public int getVal() { return val; }
        public void setVal(int val) { this.val = val; }
    }
    
    @RunWith(JMockit.class)
    public class SampleTest {
        @Tested
        private Sample sample;
    
        @Test
        public void testStaticGetPrivateOne() {
            new Expectations(sample) {
                {
                    Deencapsulation.invoke(sample, "staticGetOne");
                    result = 2;
                }
            };
    
            Assert.assertTrue(sample.staticGetPrivateOne() == 2);
        }
    
        @Test
        public void testGetPrivateOne() {
            new Expectations(sample) {
                {
                    Deencapsulation.invoke(sample, "getOne");
                    result = 2;
                }
            };
    
            Assert.assertTrue(sample.getPrivateOne() == 2);
        }
    
        @Test
        public void testGetSampleDependVal(@Injectable SampleDepend sampleDepend) {
            new Expectations(sampleDepend) {
                {
                    sampleDepend.getVal();
                    result = 2;
                }
            };
            Assert.assertTrue(sample.getSampleDependVal() == 2);
            Assert.assertTrue(sample.getSampleDependVal() == 0);
        }
    
        @Test
        public void testExecute(@Injectable SampleDepend sampleDepend) {
            sample.execute();
            new Verifications() {
                {
                    sampleDepend.getVal();
                    times = 1;
                    Deencapsulation.invoke(sample, "doExecute");
                    times = 1;
                }
            };
        }
    
        @Test
        public void testInterfaceCallWithInjectable(@Injectable Sample.SampleInterface sampleInterface) {
            Deencapsulation.setField(sample, "sampleInterface", sampleInterface);
            new Expectations() {
                {
                    sampleInterface.call();
                    result = 2;
                }
            };
            Assert.assertTrue(sample.interfaceCall() == 2);
        }
    
        @Test
        public void testInterfaceCallWithCapturing(@Capturing Sample.SampleInterface sampleInterface) {
            new Expectations() {
                {
                    sampleInterface.call();
                    result = 2;
                }
            };
            Assert.assertTrue(sample.interfaceCall() == 2);
        }
    }
    

    一个完整的PDF

    相关文章

      网友评论

          本文标题:JMockit单元测试用法

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