了解Mock
单测是软件开发中比不可少的工序,高效的测试方法,良好的测试习惯,全面的测试范围保证了代码的质量。
在测试方法中mock测试是Java程序员常用的一种测试方法。mock本意是对于一些不容易构造/获取的对象,创建一个mock对象来模拟对象的行为。mock对象就是真实对象在调试期间的代替品。
什么场景使用moke测试?
- 1、当另一方接口或服务还未完成,阻碍项目进度时,可以通过mock的方式,实现并行开发。
- 2、另一方接口不稳定,而这边需要一个稳定的结果才能继续往下走流程时,也可以使用mock。有时候自动化测试需要一个持续稳定的环境,也可以对不是很重要的服务进行mock处理。
- 3、需要模拟异常情况,但是这种异常不容易触发时,可以使用mock实现。
Jmock入门
JMockit是一款Java类/接口/对象的Mock工具,目前广泛应用于Java应用程序的单元测试中。主要特点是Api操作简单,容易上手。
环境依赖:pom.xml文件配置
<!-- 先声明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>
mock对象可以包括:类,实例对象,接口以及开源框架中的场景SpringBean、Dubbo中的bean、MQ中的消费者等等
下面以常用的类,实例对象,接口为演示示例:
mock类使用Expectations简单示例:
/**
* 普通类包含static方法,普通方法,final方法,private方法,调用private方法
*/
public class AnOrdinary {
// 静态方法
public static int staticMethod() {
return 1;
}
// 普通方法
public int ordinaryMethod() {
return 2;
}
// final方法
public final int finalMethod() {
return 3;
}
// private方法
private int privateMethod() {
return 5;
}
// 调用private方法
public int callPrivateMethod() {
return privateMethod();
}
}
/**
* 测试类
*/
@RunWith(JMockit.class)
public class ClassMockingByExpectationsTest {
private static ClassMockingByExpectationsTest JNITools;
@Test
public void testClassMockingByExpectation() {
final AnOrdinary instanceToRecord = new AnOrdinary();
new Expectations(AnOrdinary.class) {
{
// mock静态方法
AnOrdinary.staticMethod();
result = 10;
// mock普通方法
instanceToRecord.ordinaryMethod();
result = 20;
// mock final方法
instanceToRecord.finalMethod();
result = 30;
}
};
AnOrdinary instance = new AnOrdinary();
Assert.assertTrue(AnOrdinary.staticMethod() == 10);
System.out.println(AnOrdinary.staticMethod());
Assert.assertTrue(instance.ordinaryMethod() == 20);
System.out.println(instance.ordinaryMethod());
Assert.assertTrue(instance.finalMethod() == 30);
System.out.println(instance.finalMethod());
Assert.assertTrue(instance.callPrivateMethod() == 5);
System.out.println(instance.callPrivateMethod());
}
}
在上述代码中,Expectations构造了AnOrdinary类的测试返回值。其中callPrivateMethod方法并没有执行mock测试中的返回值,仍然返回代码中的初始值。
mock类mockup示例:
/**
* mockup测试类
*/
@RunWith(JMockit.class)
public class ClassMockingByMockupTest {
// AnOrdinary的MockUp类,继承MockUp即可
public static class AnOrdinaryMockUp extends MockUp<AnOrdinary> {
// Mock静态方法
@Mock
public static int staticMethod() {
return 10;
}
// Mock普通方法
@Mock
public int ordinaryMethod() {
return 20;
}
@Mock
// Mock final方法
public final int finalMethod() {
return 30;
}
// Mock private方法
@Mock
private int privateMethod() {
return 50;
}
}
@Test
public void testClassMockingByMockupT() {
new AnOrdinaryMockUp();
AnOrdinary instance = new AnOrdinary();
// 静态方法被mock了
Assert.assertTrue(AnOrdinary.staticMethod() == 10);
System.out.println(AnOrdinary.staticMethod());
// 普通方法被mock了
Assert.assertTrue(instance.ordinaryMethod() == 20);
System.out.println(instance.ordinaryMethod());
// final方法被mock了
Assert.assertTrue(instance.finalMethod() == 30);
System.out.println(instance.finalMethod());
// private方法被mock了
Assert.assertTrue(instance.callPrivateMethod() == 50);
System.out.println(instance.callPrivateMethod());
}
}
//mockup还可以以内部类的形式表示
基础mockup类 ,实现mock返回数据
mock实例和mock类方式相似,只不顾类名变成实例引用:
//mock实例
public class InstanceMockingByExpectationsTest {
@Test
public void testInstanceMockingByExpectation() {
AnOrdinaryClass instance = new AnOrdinaryClass();
// 直接把实例传给Expectations的构造函数即可Mock这个实例
new Expectations(instance) {
{
// 尽管这里也可以Mock静态方法,但不推荐在这里写。静态方法的Mock应该是针对类的
// mock普通方法
instance.ordinaryMethod();
result = 20;
// mock final方法
instance.finalMethod();
result = 30;
// native, private方法无法用Expectations来Mock
}
};
}
mock接口:
//一个普通的接口
public interface AnOrdinaryInterface {
// 方法1
public int method1();
// 方法2
public int method2();
}
//用Expectations来mock接口
public class InterfaceMockingByExpectationsTest {
// 通过@Injectable,让JMockit帮我们生成这个接口的实例,
// 一般来说,接口是给类来依赖的,我们给待测试的类加上@Tested,就可以让JMockit做依赖注入。详细见JMockit基础的章节
@Injectable
AnOrdinaryInterface anOrdinaryInterface;
@Test
public void testInterfaceMockingByExpectation() {
// 录制
new Expectations() {
{
anOrdinaryInterface.method1();
result = 10;
anOrdinaryInterface.method2();
result = 20;
}
};
Assert.assertTrue(anOrdinaryInterface.method1() == 10);
Assert.assertTrue(anOrdinaryInterface.method2() == 20);
}
}
//用MockUp来mock接口
public class InterfaceMockingByMockUpTest {
@Test
public void testInterfaceMockingByMockUp() {
// 手工通过MockUp创建这个接口的实例
AnOrdinaryInterface anOrdinaryInterface = new MockUp<AnOrdinaryInterface>(AnOrdinaryInterface.class) {
// 对方法Mock
@Mock
public int method1() {
return 10;
}
@Mock
public int method2() {
return 20;
}
}.getMockInstance();
Assert.assertTrue(anOrdinaryInterface.method1() == 10);
Assert.assertTrue(anOrdinaryInterface.method2() == 20);
}
}
原理分析:
熟悉Jmock的使用方法,在回过头来看看具体的实现逻辑, 录制(ExpectationsTransformer)和伪类(ClassLoadingBridgeFields)是Jmockit两种类型的测试方法
基于行为的Mock方式:用于对new Expectations(){{}},new Verifications(){{}},匿名类进行重定义。用于支持测试程序中的录制,重放,校验。基本步骤为:.录制方法预期行为;真实调用;验证录制的行为被调用。
基于状态的Mock方式: 即new MockUp<T> {}的匿名类或 extends MockUp<T>的子类。用于伪类的@Mock方法提供支持。 通过识别伪类@Mock方法,在对应的方法体中织入一段分支,用于走伪类的@Mock方法逻辑。
参考博客:https://www.jianshu.com/p/de8a3dc04f82
https://blog.csdn.net/chjttony/article/details/17838693
Jmcok中文网:http://jmockit.cn/