Jmockit 使用教程

Jmockit配置

在Maven pom.xml配置

  <dependency>
    <groupId>org.jmockit</groupId>
    <artifactId>jmockit</artifactId>
    <version>1.36</version>
    <scope>test</scope>
  </dependency>

JUnit4.x及以下用户特别注意事项

<!-- 先声明jmockit的依赖 -->
   <dependency>
    <groupId>org.jmockit</groupId>
    <artifactId>jmockit</artifactId>
    <version>1.36</version>
    <scope>test</scope>
  </dependency>
<!-- 再声明junit的依赖 -->
   <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.9</version>
    <scope>test</scope>
  </dependency>

就是要确保 jmockit 的依赖在 Junit 依赖之前

JMockit Coverage配置

pom文件中

 <plugin>
     <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
    <argLine>-javaagent:"${settings.localRepository}/org/jmockit/jmockit/1.36/jmockit-1.36.jar=coverage"</argLine>
    <disableXmlReport>false</disableXmlReport>
    <systemPropertyVariables>
    <coverage-output>html</coverage-output>
    <coverage-outputDir>${project.build.directory}/codecoverage-output</coverage-outputDir>
    <coverage-metrics>all</coverage-metrics>
    </systemPropertyVariables>
    </configuration>
 </plugin>

Jmockit 程序结构

代码结构

//JMockit的程序结构
public class ProgramConstructureTest {
 
    // 这是一个测试属性
    @Mocked
    HelloJMockit helloJMockit;
 
    @Test
    public void test1() {
        // 录制(Record)
        new Expectations() {
            {
                helloJMockit.sayHello();
                // 期待上述调用的返回是"hello,david",而不是返回"hello,JMockit"
                result = "hello,david";
            }
        };
        // 重放(Replay)
        String msg = helloJMockit.sayHello();
        Assert.assertTrue(msg.equals("hello,david"));
        // 验证(Verification)
        new Verifications() {
            {
                helloJMockit.sayHello();
 
                times = 1;
            }
        };
    }
 
    @Test
    public void test2(@Mocked HelloJMockit helloJMockit /* 这是一个测试参数 */) {
        // 录制(Record)
        new Expectations() {
            {
                helloJMockit.sayHello();
                // 期待上述调用的返回是"hello,david",而不是返回"hello,JMockit"
                result = "hello,david";
            }
        };
        // 重放(Replay)
        String msg = helloJMockit.sayHello();
        Assert.assertTrue(msg.equals("hello,david"));
        // 验证(Verification)
        new Verifications() {
            {
                helloJMockit.sayHello();
                // 验证helloJMockit.sayHello()这个方法调用了1次
                times = 1;
            }
        };
    }
}

JMockit的程序结构包含了测试属性或测试参数,测试方法,测试方法体中又包含录制代码块,重放测试逻辑,验证代码块

测试属性和测试参数

  1. 测试属性:即测试类的一个属性。它作用于测试类的所有测试方法。
    在JMockit中,我们可以用JMockit的注解API来修饰它。这些API有@Mocked, @Tested, @Injectable,@Capturing。在上述例子中,我们用@Mocked修饰了测试属性HelloJMockit helloJMockit,表示helloJMockit这个测试属性,它的实例化,属性赋值,方法调用的返回值全部由JMockit来接管,接管后,helloJMockit的行为与HelloJMockit类定义的不一样了,而是由录制脚本来定义了。

  2. 测试参数:即测试方法的参数。它仅作用于当前测试方法。
    给测试方法加参数,原本在JUnit中是不允许的,但是如果参数加了JMockit的注解API(@Mocked, @Tested, @Injectable,@Capturing),则是允许的。
    测试参数与测试属性的不同,主要是作用域的不同。

Jmockit注解API(@Mocked, @Tested, @Injectable,@Capturing)

@Mocked

当@Mocked修饰一个类时

 //@Mocked注解用途
public class MockedClassTest {
    // 加上了JMockit的API @Mocked, JMockit会帮我们实例化这个对象,不用担心它为null
    @Mocked
    Locale locale;
 
    // 当@Mocked作用于class
    @Test
    public void testMockedClass() {
        // 静态方法不起作用了,返回了null
        Assert.assertTrue(Locale.getDefault() == null);
        // 非静态方法(返回类型为String)也不起作用了,返回了null
        Assert.assertTrue(locale.getCountry() == null);
        // 自已new一个,也同样如此,方法都被mock了
        Locale chinaLocale = new Locale("zh", "CN");
        Assert.assertTrue(chinaLocale.getCountry() == null);
    }
  
}

当@Mocked修饰一个接口/抽象类时

//@Mocked注解用途
public class MockedInterfaceTest {
 
    // 加上了JMockit的API @Mocked, JMockit会帮我们实例化这个对象,尽管这个对象的类型是一个接口,不用担心它为null
    @Mocked
    HttpSession session;
 
    // 当@Mocked作用于interface
    @Test
    public void testMockedInterface() {
        // (返回类型为String)也不起作用了,返回了null
        Assert.assertTrue(session.getId() == null);
        // (返回类型为原始类型)也不起作用了,返回了0
        Assert.assertTrue(session.getCreationTime() == 0L);
        // (返回类型为原非始类型,非String,返回的对象不为空,这个对象也是JMockit帮你实例化的,同样这个实例化的对象也是一个Mocked对象)
        Assert.assertTrue(session.getServletContext() != null);
        // Mocked对象返回的Mocked对象,(返回类型为String)的方法也不起作用了,返回了null
        Assert.assertTrue(session.getServletContext().getContextPath() == null);
    }
}

@Mocked功能总结

@Mocked修饰的类/接口,是告诉JMockit,帮我生成一个Mocked对象,这个对象方法(包含静态方法)返回默认值。
即如果返回类型为原始类型(short,int,float,double,long)就返回0,如果返回类型为String就返回null,如果返回类型是其它引用类型,则返回这个引用类型的Mocked对象(这一点,是个递归的定义,需要好好理解一下)。

什么测试场景,我们要使用@Mocked

当我们的测试程序依赖某个接口时,用@Mocked非常适合了。只需要@Mocked一个注解,JMockit就能帮我们生成这个接口的实例。
比如在分布式系统中,我们的测试程序依赖某个接口的实例是在远程服务器端时,我们在本地构建是非常困难的,此时就交给@Mocked,就太轻松啦!

@Injectable

@Injectable 与 @Mocked的不同

//@Mocked与@Injectable的不同
public class MockedAndInjectable {
 
    @Test
    public void testMocked(@Mocked Locale locale) {
        // 静态方法不起作用了,返回了null
        Assert.assertTrue(Locale.getDefault() == null);
        // 非静态方法(返回类型为String)也不起作用了,返回了null
        Assert.assertTrue(locale.getCountry() == null);
        // 自已new一个,也同样如此,方法都被mock了
        Locale chinaLocale = new Locale("zh", "CN");
        Assert.assertTrue(chinaLocale.getCountry() == null);
    }
 
    @Test
    public void testInjectable(@Injectable Locale locale) {
        // 静态方法不mock
        Assert.assertTrue(Locale.getDefault() != null);
        // 非静态方法(返回类型为String)也不起作用了,返回了null,但仅仅限于locale这个对象
        Assert.assertTrue(locale.getCountry() == null);
        // 自已new一个,并不受影响
        Locale chinaLocale = new Locale("zh", "CN");
        Assert.assertTrue(chinaLocale.getCountry().equals("CN"));
    }
}

@Injectable 也是告诉 JMockit生成一个Mocked对象,但@Injectable只是针对其修饰的实例,而@Mocked是针对其修饰类的所有实例。
此外,@Injectable对类的静态方法,构造函数没有影响。因为它只影响某一个实例嘛!

@Tested

@Tested & @Injectable 两个好基友,通常搭配使用

// 邮件服务类,用于发邮件
public interface MailService {
 
    /**
     * 发送邮件
     * 
     * @param userId
     *            邮件接受人id
     * @param content
     *            邮件内容
     * @return 发送成功了,就返回true,否则返回false
     */
    public boolean sendMail(long userId, String content);
}
// 用户身份校验  
public interface UserCheckService {
 
    /**
     * 校验某个用户是否是合法用户
     * 
     * @param userId
     *            用户ID
     * @return 合法的就返回true,否则返回false 
     */
    public boolean check(long userId);
}
//订单服务类 ,用于下订单
public class OrderService {
    // 邮件服务类,用于向某用户发邮件。
    MailService mailService;
    // 用户身份校验类,用于校验某个用户是不是合法用户
    @Resource
    UserCheckService userCheckService;
 
    // 构造函数
    public OrderService(MailService mailService) {
        this.mailService = mailService;
    }
 
    /**
     * 下订单
     * 
     * @param buyerId
     *            买家ID
     * @param itemId
     *            商品id
     * @return 返回 下订单是否成功
     */
    public boolean submitOrder(long buyerId, long itemId) {
        // 先校验用户身份
        if (!userCheckService.check(buyerId)) {
            // 用户身份不合法
            return false;
        }
        // 下单逻辑代码,
        // 省略...
        // 下单完成,给买家发邮件
        if (!this.mailService.sendMail(buyerId, "下单成功")) {
            // 邮件发送成功
            return false;
        }
        return true;
    }
}

假设现在我们需要测试OrderService类的submitOrder方法,可是OrderService依赖MailService,UserCheckService类,
在测试过程中,我们并不想真正连结邮件服务器,也不想连结校验用户身份的服务器校验用户身份,怎么办呢?
此时@Tested与@Injectable就排上用场了!

    //@Tested与@Injectable搭配使用
public class TestedAndInjectable {
   //@Tested修饰的类,表示是我们要测试对象,在这里表示,我想测试订单服务类。JMockit也会帮我们实例化这个测试对象
   @Tested
   OrderService orderService;
   //测试用户ID
   long testUserId = 123456l;
   //测试商品id
   long testItemId = 456789l;
 
   // 测试注入方式
   @Test
   public void testSubmitOrder(@Injectable MailService mailService, 
     @Injectable UserCheckService userCheckService) {
    new Expectations() {
       {
         // 当向testUserId发邮件时,假设都发成功了
         mailService.sendMail(testUserId, anyString);
         result = true;
        // 当检验testUserId的身份时,假设该用户都是合法的
         userCheckService.check(testUserId);
        result = true;
         }
         };
    // JMockit帮我们实例化了mailService了,并通过OrderService的构造函数,注入到orderService对象中。 
    //JMockit帮我们实例化了userCheckService了,并通过OrderService的属性,注入到orderService对象中。 
    Assert.assertTrue(orderService.submitOrder(testUserId, testItemId));
    }
}
// An highlighted block
var foo = 'bar';
// An highlighted block
var foo = 'bar';
// An highlighted block
var foo = 'bar';

@Tested & @Injectable功能总结

@Injectable 也表示一个Mocked对象,相比@Mocked,只不过只影响类的一个实例。而@Mocked默认是影响类的所有实例。
@Tested表示被测试对象。如果该对象没有赋值,JMockit会去实例化它,若@Tested的构造函数有参数,
则JMockit通过在测试属性&测试参数中查找@Injectable修饰的Mocked对象注入@Tested对象的构造函数来实例化,
不然,则用无参构造函数来实例化。除了构造函数的注入,JMockit还会通过属性查找的方式,把@Injectable对象注入到@Tested对象中。
注入的匹配规则:先类型,再名称(构造函数参数名,类的属性名)。若找到多个可以注入的@Injectable,则选择最优先定义的@Injectable对象。
当然,我们的测试程序要尽量避免这种情况出现。因为给哪个测试属性/测试参数加@Injectable,是人为控制的。

什么测试场景,我们要使用@Tested & @Injectable

显然,当我们需要手工管理被测试类的依赖时,就需要用到@Tested & @Injectable。
两者搭配起来用,JMockit就能帮我们轻松搞定被测试类及其依赖注入细节。

@Capturing

Capturing主要用于子类/实现类的Mock

@Capturing平时较少用到,但某些场景下,还非用它不可。 举个例子:通常我们的系统中,都有权限校验。我们通常用AOP来做权限校验,对不?可是AOP生成的类是哪个,连类名都不知道,还怎么Mock? AOP生成的类是动态生成的。可是我们在单元测试时,不希望程序卡在权限校验上(除非是为了测试权限的测试程序)。这种情况下怎么办?用@Capturing !

//权限类,校验用户没有权限访问某资源
public interface IPrivilege {
    /**
     * 判断用户有没有权限
     * 
     * @param userId
     * @return 有权限,就返回true,否则返回false
     */
    public boolean isAllow(long userId);
 
}
//@Capturing注解用途
public class CapturingTest {
    // 测试用户ID
    long testUserId = 123456l;
    // 权限检验类,可能是人工写的
    IPrivilege privilegeManager1 = new IPrivilege() {
        @Override
        public boolean isAllow(long userId) {
            if (userId == testUserId) {
                return false;
            }
            return true;
        }
    };
    // 权限检验类,可能是JDK动态代理生成。我们通常AOP来做权限校验。
    IPrivilege privilegeManager2 = (IPrivilege) Proxy.newProxyInstance(IPrivilege.class.getClassLoader(),
            new Class[] { IPrivilege.class }, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) {
                    if ((long) args[0] == testUserId) {
                        return false;
                    }
                    return true;
                }
            });
 
    // 有Cautring情形
    @Test
    public void testCaputring(@Capturing IPrivilege privilegeManager) {
        // 加上了JMockit的API @Capturing,
        // JMockit会帮我们实例化这个对象,它除了具有@Mocked的特点,还能影响它的子类/实现类
        new Expectations() {
            {
                // 对IPrivilege的所有实现类录制,假设测试用户有权限
                privilegeManager.isAllow(testUserId);
                result = true;
            }
        };
        // 不管权限校验的实现类是哪个,这个测试用户都有权限
        Assert.assertTrue(privilegeManager1.isAllow(testUserId));
        Assert.assertTrue(privilegeManager2.isAllow(testUserId));
    }
    // 没有Cautring情形
    @Test
    public void testWithoutCaputring() {
        // 不管权限校验的实现类是哪个,这个测试用户没有权限
        Assert.assertTrue(!privilegeManager1.isAllow(testUserId));
        Assert.assertTrue(!privilegeManager2.isAllow(testUserId));
    }
}

什么测试场景用@Capturing

我们只知道父类或接口时,但我们需要控制它所有子类的行为时,子类可能有多个实现(可能有人工写的,也可能是AOP代理自动生成时)。就用@Capturing。

总结

  1. @Mocked 修饰的类或者接口 会 帮我们模拟一个 Mocked对象,所有的方法包括静态方法都是返回默认值.
    如果返回类型是基本类型(short int float long double)返回 0,如果是string类型返回null,如果是其他引用类型就返回引用类型的Mocked对象(递归mocked对象),新new出来的对象依然被Mocked修饰
  2. @Injectable 只影响被其修饰的实例,不影响静态方法,影响的非静态方法返回值跟Mocked一样,但仅限于被 Injectable修饰的这一个实例对象,新new的不起作用
  3. @Tested 表示被测试对象,如果该对象没有赋值,JMockit会去实例化它。
    若@Tested的构造函数有参数,则JMockit通过在测试属性&测试参数(即被注解API修饰的属性参数)中查找@Injectable修饰的Mocked对象注入@Tested对象的构造函数来实例化
    不然,则用无参构造函数来实例化
    除了构造函数的注入,JMockit还会通过属性查找的方式,把@Injectable对象注入到@Tested对象中
    注入的匹配规则:先类型,再名称(构造函数参数名,类的属性名)。若找到多个可以注入的@Injectable,则选择最优先定义的@Injectable对象。
  4. @Capturing 被其修饰的属性参数,JMockit会帮我们实例化这个对象,它除了具有@Mocked的特点,还能影响它的子类/实现类,即对这个属性录制的内容会对所有的子类实现有效果

Expectations录制行为结果

代码形式

new Expectations() {
    // 这是一个Expectations匿名内部类
    {
          // 这是这个内部类的初始化代码块,我们在这里写录制脚本,脚本的格式要遵循下面的约定
        //方法调用(可是类的静态方法调用,也可以是对象的非静态方法调用)
        //result赋值要紧跟在方法调用后面
        //...其它准备录制脚本的代码
        //方法调用
        //result赋值
    }
};
 
还可以再写new一个Expectations,只要出现在重放阶段之前均有效。
new Expectations() {
      
    {
         //...录制脚本
    }
};

Expectations主要有两种使用方式1

通过引用外部类的Mock对象(@Injectabe,@Mocked,@Capturing)来录制

//Expectations对外部类的mock对象进行录制
public class ExpectationsTest {
    @Mocked
    Calendar cal;
 
    @Test
    public void testRecordOutside() {
        new Expectations() {
            {
                // 对cal.get方法进行录制,并匹配参数 Calendar.YEAR
                cal.get(Calendar.YEAR);
                result = 2016;// 年份不再返回当前小时。而是返回2016年
                // 对cal.get方法进行录制,并匹配参数 Calendar.HOUR_OF_DAY
                cal.get(Calendar.HOUR_OF_DAY);
                result = 7;// 小时不再返回当前小时。而是返回早上7点钟
            }
        };
        Assert.assertTrue(cal.get(Calendar.YEAR) == 2016);
        Assert.assertTrue(cal.get(Calendar.HOUR_OF_DAY) == 7);
        // 因为没有录制过,所以这里月份返回默认值 0
        Assert.assertTrue(cal.get(Calendar.DAY_OF_MONTH) == 0);
    }
 
}

在这个例子中,在Expectations匿名内部类的初始代码块中,我们可以对外部类的任意成员变量,方法进行调用。没有录制的行为以默认的结果返回

Expectations主要有两种使用方式2

通过构建函数注入类/对象来录制
在上面的例子中,我们通过引用外部类的Mock对象(@Injectabe,@Mocked,@Capturing)来录制,可是无论是@Injectabe,@Mocked,@Capturing哪种Mock对象,都是对类的方法都mock了,可是有时候,我们只希望JMockit只mock类/对象的某一个方法。怎么办? 看下面的例子就明白啦。


//通过Expectations对其构造函数mock对象进行录制
public class ExpectationsConstructorTest2 {
 
    // 把类传入Expectations的构造函数
    @Test
    public void testRecordConstrutctor1() {
        Calendar cal = Calendar.getInstance();
        // 把待Mock的类传入Expectations的构造函数,可以达到只mock类的部分行为的目的
        new Expectations(Calendar.class) {
            {
                // 只对get方法并且参数为Calendar.HOUR_OF_DAY进行录制
                cal.get(Calendar.HOUR_OF_DAY);
                result = 7;// 小时永远返回早上7点钟
            }
        };
        Calendar now = Calendar.getInstance();
        // 因为下面的调用mock过了,小时永远返回7点钟了
        Assert.assertTrue(now.get(Calendar.HOUR_OF_DAY) == 7);
        // 因为下面的调用没有mock过,所以方法的行为不受mock影响,
        Assert.assertTrue(now.get(Calendar.DAY_OF_MONTH) == (new Date()).getDate());
    }
 
    // 把对象传入Expectations的构造函数
    @Test
    public void testRecordConstrutctor2() {
        Calendar cal = Calendar.getInstance();
        // 把待Mock的对象传入Expectations的构造函数,可以达到只mock类的部分行为的目的,但只对这个对象影响
        new Expectations(cal) {
            {
                // 只对get方法并且参数为Calendar.HOUR_OF_DAY进行录制
                cal.get(Calendar.HOUR_OF_DAY);
                result = 7;// 小时永远返回早上7点钟
            }
        };
 
        // 因为下面的调用mock过了,小时永远返回7点钟了
        Assert.assertTrue(cal.get(Calendar.HOUR_OF_DAY) == 7);
        // 因为下面的调用没有mock过,所以方法的行为不受mock影响,
        Assert.assertTrue(cal.get(Calendar.DAY_OF_MONTH) == (new Date()).getDate());
 
        // now是另一个对象,上面录制只对cal对象的影响,所以now的方法行为没有任何变化
        Calendar now = Calendar.getInstance();
        // 不受mock影响
        Assert.assertTrue(now.get(Calendar.HOUR_OF_DAY) == (new Date()).getHours());
        // 不受mock影响
        Assert.assertTrue(now.get(Calendar.DAY_OF_MONTH) == (new Date()).getDate());
    }
}

new Expectations() 的 括号中传入的是 实例就对 这个实例的 录制的方法有影响
new Expectations() 的 括号中传入的是 类,就对这个类的所有的 录制的方法都有影响

MockUp & @Mock

MockUp & @Mock提供的Mock方式,程序员比较喜欢。因为它的Mock方式最直接。

代码形式

//Mockup & @Mock的Mock方式
public class MockUpTest {
 
    @Test
    public void testMockUp() {
        // 对Java自带类Calendar的get方法进行定制
        // 只需要把Calendar类传入MockUp类的构造函数即可
        new MockUp<Calendar>(Calendar.class) {
            // 想Mock哪个方法,就给哪个方法加上@Mock, 没有@Mock的方法,不受影响
            @Mock
            public int get(int unit) {
                if (unit == Calendar.YEAR) {
                    return 2017;
                }
                if (unit == Calendar.MONDAY) {
                    return 12;
                }
                if (unit == Calendar.DAY_OF_MONTH) {
                    return 25;
                }
                if (unit == Calendar.HOUR_OF_DAY) {
                    return 7;
                }
                return 0;
            }
        };
        // 从此Calendar的get方法,就沿用你定制过的逻辑,而不是它原先的逻辑。
        Calendar cal = Calendar.getInstance(Locale.FRANCE);
        Assert.assertTrue(cal.get(Calendar.YEAR) == 2017);
        Assert.assertTrue(cal.get(Calendar.MONDAY) == 12);
        Assert.assertTrue(cal.get(Calendar.DAY_OF_MONTH) == 25);
        Assert.assertTrue(cal.get(Calendar.HOUR_OF_DAY) == 7);
        // Calendar的其它方法,不受影响
        Assert.assertTrue((cal.getFirstDayOfWeek() == Calendar.MONDAY));
 
    }
}

Mock方式是不是很简单,直接 ? 难怪很多程序员们都喜欢用MockUp & @Mock了。

那是不是我们只需要掌握MockUp & @Mock就可以了?就不需要了解其它Mock API了?

当然不是! 掌握了MockUp & @Mock的确能帮我们解决大部分的Mock场景,因为其使用方式最直接嘛。

比如下面的场景是MockUp & @Mock做不到的。

1.一个类有多个实例。只对其中某1个实例进行mock。
最新版的JMockit已经让MockUp不再支持对实例的Mock了。1.19之前的老版本仍支持。
2.AOP动态生成类的Mock。
3.对类的所有方法都需要Mock时,书写MockUp的代码量太大。
比如web程序中,经常需要对HttpSession进行Mock。若用MockUp你要写大量的代码,可是用@Mocked就一行代码就可以搞定。

MockUp & @Mock的确是好东西,在实际Mock场景中,我们需要灵活运用JMockit其它的Mock API。让我们的Mock程序简单,高效。

MockUp & @Mock比较适合于一个项目中,用于对一些通用类的Mock,以减少大量重复的new Exceptations{{}}代码。

Verifications

Verifications是用于做验证。验证Mock对象(即@Moked/@Injectable@Capturing修饰的或传入Expectation构造函数的对象)有没有调用过某方法,调用了多少次。与Exceptations的写法相似

形式

new Verifications() {
    // 这是一个Verifications匿名内部类
    {
          // 这个是内部类的初始化代码块,我们在这里写验证脚本,脚本的格式要遵循下面的约定
        //方法调用(可是类的静态方法调用,也可以是对象的非静态方法调用)
        //times/minTimes/maxTimes 表示调用次数的限定要求。赋值要紧跟在方法调用后面,也可以不写(表示只要调用过就行,不限次数)
        //...其它准备验证脚本的代码
        //方法调用
        //times/minTimes/maxTimes 赋值
    }
};
  
还可以再写new一个Verifications,只要出现在重放阶段之后均有效。
new Verifications() {
       
    {
         //...验证脚本
    }
};

例子:

//Verification的使用
public class VerificationTest {
    @Test
    public void testVerification() {
        // 录制阶段
        Calendar cal = Calendar.getInstance();
        new Expectations(Calendar.class) {
            {
                // 对cal.get方法进行录制,并匹配参数 Calendar.YEAR
                cal.get(Calendar.YEAR);
                result = 2016;// 年份不再返回当前小时。而是返回2016年
                cal.get(Calendar.HOUR_OF_DAY);
                result = 7;// 小时不再返回当前小时。而是返回早上7点钟
            }
        };
        // 重放阶段
        Calendar now = Calendar.getInstance();
        Assert.assertTrue(now.get(Calendar.YEAR) == 2016);
        Assert.assertTrue(now.get(Calendar.HOUR_OF_DAY) == 7);
        // 验证阶段
        new Verifications() {
            {
                Calendar.getInstance();
                // 限定上面的方法只调用了1次,当然也可以不限定
                times = 1;
                cal.get(anyInt);
                // 限定上面的方法只调用了2次,当然也可以不限定
                times = 2;
            }
        };
 
    }
}

通常,在实际测试程序中,我们更倾向于通过JUnit/TestNG/SpringTest的Assert类对测试结果的验证, 对类的某个方法有没调用,调用多少次的测试场景并不是太多。因此在验证阶段,我们完全可以用JUnit/TestNG/SpringTest的Assert类取代new Verifications() {{}}验证代码块。

除非,你的测试程序关心类的某个方法有没有调用,调用多少次,你可以使用new Verifications() {{}}验证代码块。

如果你还关心方法的调用顺序,你可以使用new VerificationsInOrder() {{}} .这里就不做详细的介绍了。

VerificationsInOrder

//练习经过测试的代码。
codeUnderTest.doSomething(); 
//现在,验证预期的调用是否以给定的顺序发生。
new VerificationsInOrder() {
{    
	mock1.firstExpectedMethod(anyInt); 
	minTimes = 1;
    mock2.secondExpectedMethod(1, "test"); 
    maxTimes = 2;    
    MockedClass.finalMethod(anyString); 
}
}; 

相关文档: http://javadox.com/org.jmockit/jmockit/1.12/mockit/VerificationsInOrder.html.

  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JMockit is a Java library that provides support for mocking and testing. The @Qualifier annotation is used in JMockit to identify a specific instance of a bean to be used in a test. In Spring, the @Qualifier annotation is used in a similar way to identify a specific bean to be injected into a component. However, in JMockit, the @Qualifier annotation is used in conjunction with other annotations to specify which instance of a mock or spy object to use in a test. For example, consider a scenario where we have two implementations of a service interface and we want to mock one of them for testing. We can use the @Qualifier annotation to identify the bean to be mocked and the @Mocked annotation to create a mock object of that bean. ``` public interface MyService { String getName(); } @Service("fooService") public class FooService implements MyService { @Override public String getName() { return "Foo"; } } @Service("barService") public class BarService implements MyService { @Override public String getName() { return "Bar"; } } public class MyServiceTest { @Test public void testGetName(@Mocked @Qualifier("fooService") MyService fooService, @Mocked @Qualifier("barService") MyService barService) { new Expectations() {{ fooService.getName(); result = "Mocked Foo"; barService.getName(); result = "Mocked Bar"; }}; // Use the mocked instances of fooService and barService in the test // ... } } ``` In the above example, we have two implementations of the MyService interface, FooService and BarService, and we want to mock FooService for testing. We use the @Qualifier("fooService") annotation to identify the bean to be mocked and the @Mocked annotation to create a mock object of that bean. We also create a mock object of the BarService bean using the @Mocked and @Qualifier("barService") annotations. We can then use these mocked instances of the beans in our test.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值