使用 Mockito 修改私有属性

修改私有属性来 Mock 可能不是一种很好的测试方式, 因为属性名是动态的,但有时不得已而为了,例如下面的代码:

public class UserService {
    private ExternalApi external = ExternalApi.default();
    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public User findUserById(int id) {
        return userDao.findById(external.convertId(id));
}

测试时欲隔离对 ExternalApi 的外部依赖, 当然可以把它也作为构造函数的一个参数,这样创建 UserService 实例时就可以 Mock external 属性。不过 external 经常是不变的,所以作为方法参数的必要性也不大。这就希望能在构造出 UserService 之后对 external 私有属性进行 Mock 处理。

在 Mockito 1.x 和 2.x 下要使用不同的方式,分别使用到 Whitebox 和 FieldSetter 类,它们都来自于  mockito.internal.util.reflection 包,可见 Mockito 打心底不推荐直接使用它们,但谁叫它们是 public 的呢。还有一种方式是使用 PowerMock + Mockito, 这是后话。 阅读全文 >>

使用 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 中如下方式引入 阅读全文 >>

Mockito 如何 mock 返回值为 void 的方法

最初接触 Mockito 还思考并尝试过如何用它来 mock 返回值为 void 的方法,然而 Google 查找到的一般都会说用 doThrow() 的办法

doThrow(new RuntimeException()).when(mockObject).methodWithVoidReturn();

因为无法使用常规的 when(mockObject.foo()).thenReturn(...) 的方法。

当时我就纳闷,为何我想 mock 一个返回值为 void 的方法,却是在模拟抛出一个异常,现在想来如果一个返回值为 void 的方法,为何要去 mock 这个方法呢?

回想一个我们要 mock 一个方法的意图是什么:

  1. 在特定输入参数的情况下期待需要的输出结果(返回值)
  2. 在方法抛出某种类型异常调用者作出的反应

对于 void 返回值的方法,如果要验证有没有被调用过几次可以在事后用 verify() 方法去断言。所以基本上对于 void 返回值的方法一般可不用去 mock 它,只需用  verify() 去验证,或者就是像前面一样模拟出现异常时的情况。

所以本文并不像是去直接回答标题所示的问题: Mockito 如何 mock  返回值为  void 的方法,而是如何应对 mock  对象的  void 方法 阅读全文 >>

Mockito 中捕获 mock 对象方法的调用参数

Mockito 可以帮助我们创建 Mock 对象,mock 被调用的方法,断言调用次数,在方法参数不易确定的情况下还能帮我们捕获参数。下面是我们第一个问题:

为什么要捕获调用参数

在被 mocker 方法调用参数明确的情况下可无需捕获参数,例如,下面的情景:

如果 UserService 的 save(user) 最终操作的不是同一个对象,它的实现稍加变化如下 阅读全文 >>

Java 的参数检查与断言 - Guava Preconditions

在前一篇 Scala 的参数检查与断言: require, assert, assume 和 ensuring,捉摸 Scala 的断言时提到了 JDK 内置对断言的粗略支持,也就是 assert 语句,并且默认该特性是被关掉,需 -ea 开启。

assert object != null;
assert object != null : "object can't be null";

还进一步接触了 Scala 的 Predef 方法 require, assert, assume, 和 ensuring 是怎么检验参数与断言运算结果的,Scala 的这些方法在校验失败时相应的抛出 IllegalArgumentException 和  AssertionError 异常。

JDK  7 引入了 Objects 工具类,它的三个 T requireNotNull(T object) 方法能对参数进行 null 值检查,null 时抛出 NullPointerException

阅读全文 >>

Giter8 -- 把项目布局模板放到 GitHub 上

因为学习或做些小 Demo, 会临时建立一个项目, 项目的布局也常有类似, 不想每次为一个 Maven 项目而执行 mkdir -p example/src/{main,test}/{java,resources}, 或是通过 IDE 来创建, 于是萌生了把自己常用的项目模板放到 GitHub 上的想法. 我们当然可以把直接在 GitHub 上创建一个个项目模板仓库, 想用时只要 git clone 下来, 但克隆的总是与 GitHub 上相应的仓库有关联.

Google 了, 有不少方法能建立项目基本框架, 如

  1. 创建 Maven 项目骨架,  mvn archetype:generate -DarchetypeGroupId=.....
  2. sbt 的 np 插件可以快速生成项目目录
  3. YEOMAN 也有自己的 Generator, 很强大也复杂

再就是现在要介绍的, 比较适合于我的口味的 Giter8, 简单实用, 定义模板更是轻松自如. Giter8 是构建在 sbt launcher 之上的用于从 GitHub 或其他任何 Git 仓库中攫取项目模板的命令行工具. 模板定义简单, 支持变量的提示输入. 下面是安装, 使用, 以及建立自己的模板: 阅读全文 >>

JMockit 如何 Mock 部分方法/属性

现在的 JMockit 已经偷偷升级到了 1.23 版了,在 JVM 上的 Mock 工具中就数它最无敌了,因为它抢夺了最佳控制点  --javaagent,可以说它是无所不能的。一般我们使用 JMockit 是通过两种方式,new MockUpnew Expectations. JMock 不仅能够 Mock 类的所有方法,还能部分 Mock -- 这个是 new Expectations 的默认行为。所以这里我们来看下在使用 new Expectations 的情况下如何对类的部份静态方法或部分实例方法进行 Mock。

大致表述一下,共分为三种情况

  1. 针对类进行 Mock, 只有录制的静态方法被 Mock 住,其他的静态方法或实例方法都会调用实际实现
  2. 针对某一实例进行 Mock,只在调用该实例已录制的方法才被 Mock 住,静态方法或新建实例调用任何方法都是实际实现
  3. 针对类进行 Mock,但录制的是一个实例方法,那么该实例或任何新建实例在调用该录制方法时都会被 Mock 住

不知道上面在说什么,本来就是空洞无凭,所以还是下实例,假定要测试下面这个类,或者说是测试使用到下面类的其他类 阅读全文 >>

Scala + JUnit 怎么使用 @Rule

JUnit 是个很著名的飞行模式测试框架,即使到了 Scala 中还是免不了要用 JUnit Style 的测试方式,基于 Spec 的方式并不处处行得通,比如想要在 Scala 中使用 JMockit 框架时。

JUnit 提供给我们有两个扩展点,RunnerRule, Runner 扩展点一般被各种框架劫持了,自己搞个 @RunWith(SomeRunner.class) 可能让你无法在测试中应用框架。于是剩下了 Rule 是个更自由的扩展点,这里不讲述怎么定制自己的 Rule,而是怎么用它,怎么在 Scala 中用它。之前的一篇 JUnit 4 如何正确测试异常 中使用了 ExpectedException 这个 Rule。

Rule 的要求是: Annotates fields that reference rules or methods that return a rule. A field must be public, not static, and a subtype of TestRule (preferred) or MethodRule. A method must be public, not static, and must return a subtype of TestRule (preferred) or MethodRule. 属性或方法必须是 public 非静态的,它们的类型或返回类型必须分别是 TestRuleMethedRule

这里尝试以 Java 的方式使用另一个 Rule,TestName, 可以得到当前测试方法的名称 阅读全文 >>

JMockit 如何 mock 异常

2014-07-26 修改本文

后来发现用 JMockit 来 mock 异常根本没有之前文中描述的那么复杂,其实还是在那个 result 上,给它赋个异常实例就轻而易举的解决了,只需如此

原文可不用看下去了。


做过几篇 JMockit 使用 Expectations 来 Mock 方法,私有方法,私有属性的的日志,今天工作上突然有个需求是要 Mock 异常。现在再也不能为了跑个单元测试而去拔下网线了,也不该人为的去制造其他混乱来测试。开始是想能不能用 Expectations 来 Mock 异常,尚未发现相关的属性可以设置,没有类似 result 那样的属性,比如想像中有个 exception/throwable 属性: 阅读全文 >>

JMockit 一个 Expectations 中 Mock 多个方法

从 JMockit 系列的开篇 JMockit 之 Expectations 中了解到了一个最基本的 Mock 的写法,这里记录下在一个 Expectations 中如何同时 Mock 多个方法。基本框架是这样的:

        new Expectations(MyService.class, ExternalService.class) {
            {
                MyService.prefix("Unmi");
                result = "Welcome to website: ";
                
                ExternalService.suffix("Unmi");
                result = "http://unmi.cc";
            }
        };

Java 语法告诉我们 new Expectations(){{......}} 省略号处的代码会在 Expectations 匿名类实例初始化时被调用,那么其中对 result 的赋值便是新创建的 Expectations 匿名类实例的 result 的属性值,那两次的 result 赋值难道不是以最后一个为准吗,有点文章了。先来跑个例子,见识一下现象,由三个类组成,分别是: 阅读全文 >>