java mock私有方法_JMockit Mock 私有方法和私有属性

前面说过 JMockit 因身处前线,所以简直无不可,本节例子演示 JMockit 怎么 Mock 私有方法和私有属性,示例虽然是静态方法和属性,但因采用的是反射手法,所以这种 Deencapsulation 的 Mock 手段同样适用于公有的方法或属性,无论是否静态。

本文所用 JMockit 版本为 1.6, 可能网上所搜索的方法与此有所不同,请注意 JMockit 版本差异。仍需重复一下,运行 JMockit 的例子 classpath 上必须让 jmockit.jar 在 junit.jar 之前,或用 javaagent 参数来加载 jmockit.jar,并且 junit 要 4.8 及以上版本.

1. Mock 私有方法(非静态类似)

package cc.unmi;

public class MyService {

public static String fetchData(String name){

System.out.println("call MyService.fetchData");

return fetchDataFromDB(name);

}

private static String fetchDataFromDB(String name){

throw new RuntimeException("Not implemented yet!");

}

}

1

2

3

4

5

6

7

8

9

10

11

12

packagecc.unmi;

publicclassMyService{

publicstaticStringfetchData(Stringname){

System.out.println("call MyService.fetchData");

returnfetchDataFromDB(name);

}

privatestaticStringfetchDataFromDB(Stringname){

thrownewRuntimeException("Not implemented yet!");

}

}

fetchDataFromDB 是私有静态方法,正常测试的话肯定不过

package cc.unmi;

import mockit.Deencapsulation;

import mockit.Expectations;

import org.junit.Assert;

import org.junit.Test;

public class MyServiceTest {

@Test

public void testFetchData() {

new Expectations(MyService.class) {

{

Deencapsulation.invoke(MyService.class, "fetchDataFromDB", "Unmi");

result = "http://unmi.cc";

}

};

String actual = MyService.fetchData("Unmi");

Assert.assertEquals("http://unmi.cc", actual);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

packagecc.unmi;

importmockit.Deencapsulation;

importmockit.Expectations;

importorg.junit.Assert;

importorg.junit.Test;

publicclassMyServiceTest{

@Test

publicvoidtestFetchData(){

newExpectations(MyService.class){

{

Deencapsulation.invoke(MyService.class,"fetchDataFromDB","Unmi");

result="http://unmi.cc";

}

};

Stringactual=MyService.fetchData("Unmi");

Assert.assertEquals("http://unmi.cc",actual);

}

}

这行

Deencapsulation.invoke(MyService.class, "fetchDataFromDB", "Unmi");

使用了反射的方式拦截了 MyService 的 fetchDataFromDB 方法,并非强设了它的返回值为  “http://unmi.cc”。关键是调用了 Deencapsulation.invoke() 方法,来看到这个类里还有哪些方法:

jmockit-deencapsulation1.png

看到上图,我们必须要产生一些想法的,特别是 invoke 和 setField,以及它们的第一个参数可以是 Class,也可以是 Object,不难获知的是我们能够借助于它来 Mocket 私有方法和属性,不论它们是静态还是非静态的。

上面的测试用例可以见绿,输出为:

call MyService.fetchData

如果把测试代码中的调用处改为

String actual = MyService.fetchData("Unmis");

就要见红了,失败:

java.lang.RuntimeException: Not implemented yet!

at cc.unmi.MyService.fetchDataFromDB(MyService.java:10)

at cc.unmi.MyService.fetchData(MyService.java:6)

at cc.unmi.MyServiceTest.testFetchData(MyServiceTest.java:21)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at java.lang.reflect.Method.invoke(Method.java:606)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at java.lang.reflect.Method.invoke(Method.java:606)

................

对了,就是反射。受上面的点拨,怎么 Mock 属性已了然于心了,再行累述纯为拉长篇幅,不妨

2. Mock 私有属性(静态亦类似)

package cc.unmi;

public class MyService {

private String url = "";

public String fetchData(String name){

System.out.println("call MyService.fetchData");

return this.url;

}

}

1

2

3

4

5

6

7

8

9

10

11

packagecc.unmi;

publicclassMyService{

privateStringurl="";

publicStringfetchData(Stringname){

System.out.println("call MyService.fetchData");

returnthis.url;

}

}

欲 Mock 的是 url 属性,从 fetchData 方法的 return url 可以检测到 Mock 后的值

package cc.unmi;

import mockit.Deencapsulation;

import mockit.Expectations;

import org.junit.Assert;

import org.junit.Test;

public class MyServiceTest {

@Test

public void testFetchData() {

final MyService service = new MyService();

Deencapsulation.setField(service, "url", "http://unmi.cc");

String actual = service.fetchData("Unmi");

Assert.assertEquals("http://unmi.cc", actual);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

packagecc.unmi;

importmockit.Deencapsulation;

importmockit.Expectations;

importorg.junit.Assert;

importorg.junit.Test;

publicclassMyServiceTest{

@Test

publicvoidtestFetchData(){

finalMyServiceservice=newMyService();

Deencapsulation.setField(service,"url","http://unmi.cc");

Stringactual=service.fetchData("Unmi");

Assert.assertEquals("http://unmi.cc",actual);

}

}

断言成功,也用不着设置 result 值了, 而且这时候也用不着 new Expectations()  了。

Deencapsulation 是后来引入的,在 JMockit 0.999.19 中可以在  Expectations 中直接调用 invoke, setField 方法来改变运行中的值,Expectations 继承自 Invocations,原来的 Invocations 中定义了 invoke, setField, getFiled 等方法,新版的 JMockit 从  Invocations 中移除了那些方法。

记得当初我们用 @MockClass, @Mock, 再 Mockit.setUpMock() 三步曲来进行 Mock 操作,如今看来光 new Expectations() 这一招便可走遍天下。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值