使用 JMockit 来 mock 构造函数

Java 测试的 Mock 框架以前是用 JMockit, 最近用了一段时间的 Mockito, 除了它流畅的书写方式,经常这也 Mock 不了,那也 Mock 不了,需要迁就于测试来调整实现代码,使得实现极不优雅。比如 Mockito 在 私有方法,final 方法,静态方法,final 类,构造方法面前统统的缴械了。powermock 虽然可作 Mockito 的伴侣来突破 Mockito 本身的一些局限,但是我一用它来 Mock 一个构造方法就出错

Caused by: java.lang.ClassNotFoundException: org.mockito.exceptions.Reporter

原因是 Mockito 变化太快,powermock 跟不上它的步伐 -- https://github.com/powermock/powermock/issues/684,于是我只能止步。

不得已再祭出 JMockit 这号称(也确实是)一无所不能的大杀器,在此见识一下它怎么 Mock 构造函数的

本篇实例所使用的 JMockit 版本是 1.30, 当前最新版 1.31, 由于尚未被 Maven 中央仓库收录,所以暂用 1.30。在 pom.xml 中如下方式引入

待测试的代码是类 Example, 代码如下

它的 fetchOneUser(category) 方法中需要根据条件来创建一个 UserService 实例,所以未把 UserService 实例声明为 Example 的属性,通过 Example 的构造函数来传入,若如此就很容易用 Mockito 的 Mock 这个 UserService 实例了。

private UserService userService;

public Exmple(UserService userService) {
    this.userService = userServie;
}

上面的实现是 Mockito 最喜爱的口味了。但由于 userService 并不跟随 Example 创建,所以 Mockito 去 Mock fetchOneUser(category) 里的 new UserService(userDao,  "admin") 就显示得捉襟见肘了。

在使用 JMockit mock UserService 构造函数之前,贴出一下 UserService 的演示实现

我们要测试的目标方法是 Example.fetchOneUser(category), 其中一个测试是 userService 实例的 findById(id) 方法获得什么它也返回什么,所以单元测试中的 service.findById(id) 方法不应该调用实际的 userDao 的相应方法,也就是我们要 Mock 的目的所在。所以本例中的 UserDao 的方法并未实现,如下

那么来看测试代码 ExampleTest

上面的测试 testFetchOneUser() 顺利通过,这里的精髓就在

new UserService(withInstance(UserDao.class), "admin");
result = userService;

JMockit 只是把构造函数当成一个普通的有返回值的方法而已。

我们也可以换一种方式来 Mock 构造函数,用  new MockUp<Class> 的方式,用以植入 Mock 的内部变量值,下面的例子不直接 Mock UserService 实例,而是通 $init 函数来改变生成的 userService 实例的内部状态,以便对它内部的操作作进一步精细的控制。$init 在 JVM 中就就构造函数的表示法。

下面的例子,Mockito 和 JMockit 双管齐下,结合 JMockit 的强悍功能,以及 Mockito 的 BBD 风格,你也可以只使用 JMockit 的 Expectations API 来 mock 对 mockedUserDao 的操作。

MockUp 和 Expectations API 其实也是 JMockit 的两种书写方式,前者可用于 mock 私有方法,后者常用,但功能稍弱一些。

类别: Java/JEE. 标签: , . 阅读(1,994). 订阅评论. TrackBack.

Leave a Reply

avatar