package com.jmokit;
import com.Jmokit;
import mockit.Expectations;
import mockit.Mocked;
import mockit.Verifications;
import org.junit.Assert;
import org.junit.Test;
public class JomckitConstructureTest {
// 测试属性,被mock掉,并不会真正执行这个属性里的方法。而是对期待的执行有一个result
// 如果没有mock掉,就会真的执行这个javabean里的方法。mock掉,就不执行方法,而是返回期待的result
// 这个期待,并不是对真正执行后的期待,而是coder给的期待
@Mocked
public Jmokit jmokit = new Jmokit();
// 测试方法
@Test
public void test1() {
// 录制(Record) 录制代码块
new Expectations() {
// 这一段为什么放在代码块里,如果不放在{}离,就没法识别jmokit
{
jmokit.sayHello();
//期望返回的result
result = "hello jjj";
}
};
// 重放测试逻辑
String msg = jmokit.sayHello();
Assert.assertTrue(msg.equals("hello jjj"));
// 验证代码块
new Verifications() {
{
jmokit.sayHello();
times = 1;
}
};
}
@Test
public void test2(@Mocked Jmokit jmokit1) {
new Expectations() {
{
jmokit1.sayHello();
result = "1111";
}
};
String msg = jmokit1.sayHello();
Assert.assertTrue(msg.equals("1111"));
}
}
Jmockit 的程序结构分为, 测试属性/ 测试参数, 测试方法, 测试方法中又包含:录制代码块,重放测试逻辑, 验证代码块
当@Mocked修饰一个类时:
Injectable 也是告诉JMockit生成一个Mocked对象,但是只针对其修饰的实例。且注解没有影响到该类的静态方法以及构造函数。
此注解通常一起使用。
场景实例:在买家下订单时,电商后台需要验证卖家身份是否合法
官网代码如下:
// 邮件服务类,用于发邮件
public interface MailService {
/**
* 发送邮件
*
* @param userId
* 邮件接受人id
* @param content
* 邮件内容
* @return 发送成功了,就返回true,否则返回false
*/
public boolean sendMail(long userId, String content);
}```
```java
// 用户身份校验
public interface UserCheckService {
/**
* 校验某个用户是否是合法用户
*
* @param userId
* 用户ID
* @return 合法的就返回true,否则返回false
*/
public boolean check(long userId);
}//订单服务类 ,用于下订单```
```java
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;
}
}```
```java
//@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));
}
}
@Tested注解只能修饰类,如果修饰interface,生成的对象是null
@Tested表示被测试对象。如果该对象没有赋值,JMockit会去实例化它,若@Tested的构造函数有参数,
则JMockit通过在测试属性&测试参数中查找@Injectable修饰的Mocked对象注入@Tested对象的构造函数来实例化,
不然,则用无参构造函数来实例化。除了构造函数的注入,JMockit还会通过属性查找的方式,把@Injectable对象注入到@Tested对象中。
注入的匹配规则:先类型,再名称(构造函数参数名,类的属性名)。若找到多个可以注入的@Injectable,则选择最优先定义的@Injectable对象。
当然,我们的测试程序要尽量避免这种情况出现。因为给哪个测试属性/测试参数加@Injectable,是人为控制的。
当我们是使用代理类,或者子类来实现其父接口的具体方法的时候。@Capturing注解除了使该class/interface 具有@Mocked注解的属性,还会影响到他的子类和实现类
两种使用方式:
这个mock方式比较实用于对一些通用类的mock,以减少大量重复的new Expectations{{}}