jmockit是一个非常强大的单元测试框架,配合Junit使用jmockit对对象进行mock
官网 中文介绍
与其他框架的比较
覆盖率配置
配置
<!-- 先声明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.12</version>
<scope>test</scope>
</dependency>
结构
通常是录制—回放—验证的步骤
通过Expectations(Object… classesOrObjectsToBePartiallyMocked)只对部分静态函数进行mock,其他方法保持原态
class ExampleTest {
@Tested ServiceAbc tested;
@Injectable DependencyXyz mockXyz;
@Test
void doOperationAbc(@Mocked AnotherDependency anyInstance) {
new Expectations() {{
anyInstance.doSomething(anyString); result = 123;
AnotherDependency.someStaticMethod(); result = new IOException();
}};
tested.doOperationAbc("some data");
new Verifications() {{ mockXyz.complexOperation(true, anyInt, null); times = 1; }};
}
}
//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;
}
};
}
}
常用注解介绍
@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注解用途
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);
}
}
@Injectable
@Injectable 也是告诉 JMockit生成一个Mocked对象,但@Injectable只是针对其修饰的实例,而@Mocked是针对其修饰类的所有实例。
此外,@Injectable对类的静态方法,构造函数没有影响。因为它只影响某一个实例嘛!
可以注入到ut的测试方法中。
//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));
}
}
mockUp的类,被mock的方法,replay的时候都执行mock的方法;没有被mock的方法,调用原有代码。
比如下面的场景是MockUp & @Mock做不到的。
一个类有多个实例。只对其中某1个实例进行mock。
最新版的JMockit已经让MockUp不再支持对实例的Mock了。1.19之前的老版本仍支持。
AOP动态生成类的Mock。
对类的所有方法都需要Mock时,书写MockUp的代码量太大。
比如web程序中,经常需要对HttpSession进行Mock。若用MockUp你要写大量的代码,可是用@Mocked就一行代码就可以搞定。