TDD 推求测试先行,不光在自己代码未实现时可以先做好测试,即使平台依赖或第三方接口未准备好我们也能先行一步的,这就要对接口依赖进行 Mock。同时 Mock 也使得我们的测试代码在运行当中不至于随着第三方接口的沦陷而坠入深渊。

Java 中 Mock 工具也不少,像通用 EasyMock, jMock, Mockito, Unitils Mock, PowerMock, 再比如偏专业的 HttpMock, StrutsMock 等。但 JMock 与前面各位相比简直是全能选手,对 final/static/native/private 方法都能 Mock,功能上还远不止这些了,可以看看一个对比图 https://code.google.com/p/jmockit/wiki/MockingToolkitComparisonMatrix。

JMockit 是基于 Java5 的 java.lang.instrument 包开发的,所以它才能夺得先机,也可陷得更深。自然它要求 JDK5 及以上,JUnit 4.8 及以上版本。命令行下原来用 -javaagent:/.../lib/jmockit.jar 加载 JMockit,现在发现把 jmockit.jar 放在 classpath 下就 OK 的,但是必须放在 junit.jar 包之前,否则你会看到这个 java.lang.IllegalStateException: JMockit wasn't properly initialized; check that jmockit.jar precedes junit.jar in the classpath。 JMockit 有两种 Mock 方式:

1. Behavior-oriented(Expectations & Verifications)  --- 基于代码执行行为的模仿,象黑盒测试

2. State-oriented(MockUp)   --- 侵入类内部,随意模仿,似白盒,可以说是能为所欲为

此篇体验下第一种 Mock 方式,在测试代码中最直观就是那个 new Expectations(...){{result = some;}},下面来看个实际的例子。应用场景是

MyService.testFetchData() 方法要调用 ExternalService.fetchDataFor() 方法来获得数据,那么我们在 ExternalService.fetchDataFor() 尚未实现之前怎么去测试 MyService.testFetchData() 方法呢,JMockit 要做的就是对 ExternalService.fetchDataFor() 的返回进行 Mock。

具体实现代码如下:

1. ExternalService.java

package cc.unmi;

public class ExternalService {

public static String fetchDataFor(String name){

System.out.println("call ExternalService.fetchDataFor");

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

}

}

1

2

3

4

5

6

7

8

packagecc.unmi;

publicclassExternalService{

publicstaticStringfetchDataFor(Stringname){

System.out.println("call ExternalService.fetchDataFor");

thrownewRuntimeException("Not implemented yet!");

}

}

方法 fetchDataFor 并未真正实现,如果测试代码真的进入到这个方法来总是会导致测试用例失败的,除非你就是想要来测试异常的,那没活说。

2. MyService.java

package cc.unmi;

public class MyService {

public static String fetchData(String name){

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

return ExternalService.fetchDataFor(name);

}

}

1

2

3

4

5

6

7

8

packagecc.unmi;

publicclassMyService{

publicstaticStringfetchData(Stringname){

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

returnExternalService.fetchDataFor(name);

}

}

3. MyServiceTest.java

package cc.unmi;

import mockit.Expectations;

import org.junit.*;

public class MyServiceTest {

@Test

public void testFetchData() {

new Expectations(ExternalService.class){

{

ExternalService.fetchDataFor("Unmi");

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

//上行或者用 returns("blog: http://unmi.cc")

}

};

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

Assert.assertEquals("blog: 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

packagecc.unmi;

importmockit.Expectations;

importorg.junit.*;

publicclassMyServiceTest{

@Test

publicvoidtestFetchData(){

newExpectations(ExternalService.class){

{

ExternalService.fetchDataFor("Unmi");

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

//上行或者用 returns("blog: http://unmi.cc")

}

};

Stringactual=MyService.fetchData("Unmi");

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

}

}

上面的代码能给予我们的想像空间很大

new Expectations(...){{}} 产生了一个匿名子类和它的实例

对该实例的 result 属性的赋值就是预设的 ExternalService.fetchDataFor("Unmi") 的返回值

运行时参数的匹配的讲究,如果匹配不上也不会调用被 Mock 的方法,参数选择上可以有 anyString, withAny(obj) 等方式来忽略某个位置上的参数匹配

Expectations 是严格的,即被 Mock 方法必须被精确命中; 同时还有一个 NonStrictExpectations, 就是说我们 Mock 的方法只作壁上观也无妨

怎么同时 Mock 多个类,多个方法呢?

还有这里怎么 Mock 私有方法,怎么 Mock 实例方法呢?

为控制篇幅,有些问题其他地方再探讨吧。

我们在命令行下执行并看到输出:

unmi@localhost$ java -classpath lib/jmockit.jar:lib/junit-4.11.jar:lib/hamcrest-core-1.3.jar:bin org.junit.runner.JUnitCore cc.unmi.MyServiceTest

JUnit version 4.11

.call MyService.fetchData

Time: 0.029

OK (1 test)

测试通过,并未实际去调用 ExternalService.fetchDataFor("Unmi"),这个被 JMockit 接管了。

如果把 MyServiceTest.java 中 MyService.fetchData("Unmi") 改成 MyService.fetchData("Hello Unmi"), 再次跑下测试就失败了

.call MyService.fetchData

call ExternalService.fetchDataFor

E

Time: 0.031

There was 1 failure:

1) testFetchData(cc.unmi.MyServiceTest)

java.lang.RuntimeException: Not implemented yet!

at cc.unmi.ExternalService.fetchDataFor(ExternalService.java:6)

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

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

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)

FAILURES!!!

Tests run: 1,  Failures: 1

参数匹配不上就跑实际方法实现去了,现在把 MyServiceTest.java 中的 ExternalService.fetchDataFor("Unmi"); 改为 ExternalService.fetchDataFor(anyString); 测试用例又可以通过。

略加延伸

Mock 实例方法时, Expectations 可以这么写:

new Expectations(){

@Mocked ExternalService service;

{

service.fetchDataFor("Unmi");

result = "all right";

}

};

1

2

3

4

5

6

7

newExpectations(){

@MockedExternalServiceservice;

{

service.fetchDataFor("Unmi");

result="all right";

}

};

因 为 Java 不强制调用 static 方法要用 Class,也可以用实例来调用 static 方法,所以上面的方式同样也适用于对 static 方法的 Mock,只是平白多出 service 实例来。基于对 new Expectations(){{...}} 的理解,MyServiceTest.java 可这么写:

public class MyServiceTest {

@Mocked ExternalService service;

@Test

public void testFetchData() {

new Expectations(service) {

{

service.fetchDataFor("Unmi");

result = "all right";

}

};

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

Assert.assertEquals("all right", actual);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

publicclassMyServiceTest{

@MockedExternalServiceservice;

@Test

publicvoidtestFetchData(){

newExpectations(service){

{

service.fetchDataFor("Unmi");

result="all right";

}

};

Stringactual=MyService.fetchData("Unmi");

Assert.assertEquals("all right",actual);

}

}

java中expectations_JMockit 之 Expectations相关推荐

  1. java中实现具有传递性吗_Java中volatile关键字详解,jvm内存模型,原子性、可见性、有序性...

    一.Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的. Java内存模型规定了所有的变量都存储在主内存中.每条线程中还有自己的工作内存,线程的工作 ...

  2. java中调用python

    在Java中调用Python </h1><div class="clear"></div><div class="postBod ...

  3. java中hashcode_浅谈Java中的Hash值

    1.Hash值有什么用? HashMap.HashTable.HashSet,所以涉及到使用Hash值进行优化存储的地方,都会用到HashCode.HashCode是Key,这种计算为提高计算的性能. ...

  4. JAVA中获取当前系统时间

    JAVA中获取当前系统时间 转自:http://www.cnblogs.com/Matrix54/archive/2012/05/01/2478158.html 一. 获取当前系统时间和日期并格式化输 ...

  5. Java中的对象和包

    什么是对象 对象就是实际生活中的事物,可以说一切事物都是对象. 对象的三个特点 1  对象的行为:这个对象能做什么, 例如包子是用来吃的 2  对象的状态:对象保持的一种状态,例如这个包子是热的还是凉 ...

  6. java中标识符,关键字,数据类型

    什么是标识符? 在java语言中用来给一个类,变量或方法命名的符号 标识符的命名规则 标识符可以由字母.数字.下划线(_).美元符($)组成,但不能包含 @.%.空格等其它特殊字符,不能以数字开头. ...

  7. Java中byte与16进制字符串的互相转换

    https://www.cnblogs.com/qinwangchen/p/5418028.html * Convert byte[] to hex string.这里我们可以将byte转换成int, ...

  8. JAVA中priorityqueue详解

    Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示.本文从Queue接口函数出发,结合生动的图解,深入浅出地分析PriorityQueue每个操作的具体过程和时间复杂度, ...

  9. 使用java中replaceAll方法替换字符串中的反斜杠

    今天在项目中使用java中replaceAll方法将字符串中的反斜杠("\")替换成空字符串(""),结果出现如下的异常: 1 java.util.regex. ...

最新文章

  1. 【剑指offer】顺时针打印矩阵
  2. Codeforces Round #Pi (Div. 2)(A,B,C,D)
  3. 在centos上,oracle数据库更改IP后无法启动监听,解决方法
  4. 【Java线程】线程协作
  5. 【MyBatis】Mybatis实现分页效果
  6. 使用自动伸缩组在AWS中运行安全数据库集群
  7. Java智能卡 技术_java智能卡APDU学习笔记
  8. Django---模版层
  9. windows2008R2+IIS部署python Django的web环境
  10. 电影里看到程序员一台电脑装2个显示屏,这样有什么优点?
  11. kepserver 6.5_油价下调!加满一箱油少花6.5元……
  12. 2010年imac从移动硬盘启动Win10
  13. matlab如何进行图像读取,matlab如何读取图像
  14. Data()笔记之getDate()的基本用法
  15. Vue.js--表单修饰符(.lazy、.number、.trim)详解
  16. 网站性能优化之CSS无图片技术
  17. Oracle11g卸载完服务还有,Oracle11g服务器与客户端卸载、安装
  18. 凯文凯利 《必然》 读书笔记
  19. leetcode 876.链表中间结点
  20. Ubuntu搜狗输入法乱码问题

热门文章

  1. JAVA软件开发之8例
  2. AI算法之NaiveBayes(基于贝努利模型)
  3. 谈JS 异步任务,微任务,宏任务
  4. c语言实习,c语言实习实例经典版.doc
  5. crs-2632 crs-2674
  6. htonl函数 c语言,c – 理解htonl()和ntohl()
  7. Java 与排序算法(2):选择排序
  8. 360手机刷机:360N6lite刷机
  9. WM8960 audio codec
  10. 零基础自学Java编程大概需要多久时间?