基础概念
- 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);
}
}
网友评论