当前位置: 首页 > 工具软件 > Mockup > 使用案例 >

jmockit教程_jmockit使用总结-MockUp重点介绍

堵雅健
2023-12-01

公司对开发人员的单元测试要求比较高,要求分支覆盖率、行覆盖率等要达到60%以上等等。项目中已经集成了jmockit这个功能强大的mock框架,学会使用这个框架势在必行。从第一次写一点不会,到完全可以应付工作要求,期间踩了好多坑,学到了不少东西。下面简单总结一下jmockit这个框架的使用,重点介绍MockUp的使用,因为项目中都采用此种方式模拟方法。

一、框架集成

添加maven依赖

org.jmockit

jmockit

1.16

test

junit

junit

4.12

二、@Mocked模拟方式介绍

@Mocked模拟,由录制、回放、验证三步骤完成,是对某个类的所有实例的所有方法进行完整的模拟方式。

/*** 被测试类*/

public classApp {publicString say() {return "Hello World";

}publicString say2(){return "Hello World 2";

}public staticString staticSay() {return "Still hello world";

}

}

/*** 测试类*/

public classAppTest {/*** 针对类及所有实例的的整体模拟,未写录制的方法默认返回0,null等*/@Mocked

App app;

@Testpublic voidtestSay() {//录制,定义被模拟的方法的返回值,可以录制多个行为,写在一个大括号里也可以,多个大括号隔开也可以

newExpectations() {{

app.say();

result= "say";

}};//回放,调用模拟的方法

System.out.println(app.say()); //say

System.out.println(new App().say()); //say

System.out.println(App.staticSay()); //null//验证

newVerifications() {{//验证say模拟方法被调用,且调用了2次

app.say();

times= 2;//验证staticSay模拟方法被调用,且调用了1次

App.staticSay();

times= 1;

}};

}

}

三、@Injectable模拟方式介绍

@Injectable和@Mocked的方式很像,区别是@Injectable仅仅对当前实例进行模拟。

/*** 测试类*/

public classAppTest001 {/*** 仅针对当前实例的整体模拟*/@Injectable

App app;

@Testpublic voidtestSay() {//录制

newExpectations() {{

app.say();

result= "say";

}};//回放

System.out.println(app.say()); //say,模拟值

System.out.println(app.say2()); //null,模拟默认值

final App appNew = newApp();

System.out.println(appNew.say());//Hello World,未被模拟,方法实际值

System.out.println(App.staticSay()); //Still hello world,未被模拟,方法实际值//验证

newVerifications() {

{//验证say模拟方法被调用

app.say();

times= 1;

}

{

appNew.say();

times= 1;

}

{//验证staticSay模拟方法被调用,且调用了1次

App.staticSay();

times= 1;

}

};

}

}

四、Expectations传参,局部模拟

/*** 测试类*/

public classAppTest002 {

@Testpublic voidtestSay() {final App app = newApp();//录制,带参数表示局部模拟【针对所有实例的局部模拟,具体到某个方法的模拟,其它方法不模拟】

new Expectations(App.class) {{

app.say();

result= "say";

}};//回放

System.out.println(app.say()); //say,模拟值

System.out.println(app.say2()); //Hello World 2 ,未被模拟,方法实际值

System.out.println(new App().say()); //say,模拟值

System.out.println(App.staticSay()); //Still hello world,未被模拟,方法实际值

}

}

五、MockUp局部模拟,可重写原有方法的逻辑,比较灵活,推荐使用

/*** 测试类*/

public classAppTest003 {

@Testpublic voidtestSay() {//局部模拟【针对所有实例的局部模拟,具体到某个方法的模拟,其它方法不模拟】

new MockUp(App.class){

@Mock

String say(){return "say";

}

};//回放

System.out.println(new App().say()); //say,模拟值

System.out.println(new App().say2()); //Hello World 2,未被模拟,方法实际值

System.out.println(App.staticSay()); //Still hello world,未被模拟,方法实际值

}

}

六、MockUp如何模拟私有方法、静态方法、静态块、构造函数等

1.模拟私有属性(实例属性和类属性),MockUp不支持,采用如下方式

//模拟实例的字段

Deencapsulation.setField(Object objectWithField, String fieldName, Object fieldValue)//模拟类的静态字段

Deencapsulation.setField(Class> classWithStaticField, String fieldName, Object fieldValue)

2.模拟私有方法,MockUp不支持,采用如下方式

//模拟实例方法,注意参数不能为null,如果要传null请使用带参数类型的另一个重载方法

Deencapsulation.invoke(Object objectWithMethod, String methodName, Object... nonNullArgs)//模拟类方法

Deencapsulation.invoke(Class> classWithStaticMethod, String methodName, Object... nonNullArgs)

3.模拟静态方法

和模拟实例方法一样,去掉static即可

4.模拟静态块

//mock静态代码块

@Mockvoid$clinit(Invocation invocation){

}

5.模拟实例块和构造函数

//mock代码块和构造函数

@Mockvoid$init(Invocation invocation) {

}

6.模拟接口,MockUp不支持,采用@Capturing

/*** 被模拟的接口*/

public interfaceIUserService {

String getUserName( );

}

public class UserServiceImpl implementsIUserService {

@OverridepublicString getUserName() {return "Bob";

}

}

/*** 接口模拟测试*/

public classIUserServiceTest {/*** MockUp不能mock接口方法,可以用来生成接口实例*/@Testpublic voidgetUserNameTest001(){

MockUp mockUp = new MockUp(){

@Mock

String getUserName( ){return "Jack";

}

};

IUserService obj= newUserServiceImpl();

System.out.println(obj.getUserName());//Bob,mock失败

obj=mockUp.getMockInstance();

System.out.println(obj.getUserName());//Jack,mockUp生成的实例,和自己写一个接口实现一样

obj= newUserServiceImpl();

System.out.println(obj.getUserName());//Bob,mock失败

}/*** @Capturing 注解可以实现mock接口,所有实现类的实例均被mock

*@parambase*/@Testpublic void getUserNameTest002(@Capturing finalIUserService base){

IUserService obj= newUserServiceImpl();

System.out.println(obj.getUserName());//mock成功,返回模拟默认值null//录制

newExpectations(){

{

base.getUserName();

result= "Jack";

}

};

System.out.println(obj.getUserName());//Jack

obj= newIUserService() {

@OverridepublicString getUserName() {return "Alice";

}

};

System.out.println(obj.getUserName());//Jack

}

}

七、MockUp模拟方法中调用原方法

/*** 模拟方法调用原方法逻辑测试*/

public classJSONObjectTest {

@Testpublic voidgetTest(){

JSONObject jsonObject= newJSONObject();

jsonObject.put("a","A");

jsonObject.put("b","B");

jsonObject.put("c","C");

System.out.println(jsonObject.get("a")); //A

System.out.println(jsonObject.get("b")); //B

System.out.println(jsonObject.get("c")); //C

new MockUp(){

@Mock

Object get(Invocation invocation,Object key){if("a".equals(key)){return "aa";

}else{//调用原逻辑

returninvocation.proceed(key);

}

}

};

System.out.println(jsonObject.get("a")); //aa

System.out.println(jsonObject.get("b")); //B

System.out.println(jsonObject.get("c")); //C

}

}

八、MockUp单元测试用例单个跑正常,批量跑失败可能的原因

1.测试方法使用了共享变量,相互影响。

2.在一个测试方法里多次MockUp同一个类,将某个类需要mock的方法均写在一个new MockUp里即可解决,原因未知。

3.在测试方法里的MockUp,在方法结束前,调用一下mockUp.tearDown。

 类似资料: