Jmock

原创 2011年01月08日 21:50:00

 

物种起源

任何框架和lib都有其实际需求来源,纯粹为了技术或概念而code的项目,大概都还在象牙塔里。

 

Jmock 的首页上第一句话相当简明:

JMock is a library that supports test-driven development of Java code with mock objects.

 

了解JMOCK,从两个概念入手:


TDD
Unit tests are so named because they each test one unit of code. Whether a module of code has hundreds of unit tests or only five is irrelevant. A test suite for use in TDD should never cross process boundaries in a program, let alone network connections. Doing so introduces delays that make tests run slowly and discourage developers from running the whole suite. Introducing dependencies on external modules or data also turns unit tests into integration tests. If one module misbehaves in a chain of interrelated modules, it is not so immediately clear where to look for the cause of the failure.

 

为什么叫unit test(单元测试)?单元测试就是测试单个method的功能,这也就是为什么我们写junit测试时,总是一个个testMethodXXX。TDD里提倡单元测试的单元性,需要将外部依赖性(比如DB, network)隔离出来。有两个好处,一是让test 飞一飞,跑的更快,二是界定boundaries,因为逻辑交叉,一个逻辑失败,会导致好多测试失败。

 

老实说,这两点有些似是而非。test越接近产品环境,越有可能发现产品环境下的bug。另外continuous integration中说测试可以放在晚上跑,所以速度慢不是问题。连锁反应是个问题,可能会导致找bug时道路曲折。但测试失败应该不是个经常发生的问题把。

 

anyway,怎么保证隔离呢?that's why need mock..

 

mock object

In a unit test, mock objects can simulate the behavior of complex, real (non-mock) objects and are therefore useful when a real object is impractical or impossible to incorporate into a unit test

 

mock object就是使用method stub来实现定义的interface,比如:

BEGIN ThermometerRead(Source insideOrOutside)
        RETURN 28
END ThermometerRead

 

方法不作任何逻辑处理,直接return一个预定的结果。

The Design principle 

 

针对上述概念,最简单直接的一种实现方法是每个接口增加一个test实现。在需要的地方使用test实现来作测试。这种方法理论上可行, why?

首先你会形成class disaster,增加无数个test class。其次缺少灵活性,在stub实现往往是输出一个固定结果,而测试时有可能需要针对不同条件来返回不同值。

 

关于class disaster的问题, jmock使用代理来避免。关于return的问题,jmock引入了action的概念。

简单点,实现原则:

  • proxy 
  • 声明方法的调用
  • 声明方法的输入/输出

其具体实现,本人到没有dig。只是对其中的一种语法比较感兴趣, Expectations使用双大括号声明{{ }} (Double-Brace Block)。java中有这种语法?本文结尾给出揭晓

 

The usage

hello, jmock

首先引入jmock的lib,使用eclipse中的maven-plugin自动添加

  • jmock-2.5.1.jar
  • hamcrest-core-1.1.jar
  • hamcrest-library-1.1.jar

可能会有类加载的问题,因为junit 里面也打包了hamcrest。如果有java.lang.SecurityException class "org.hamcrest.TypeSafeMatcher" 问题。可以将junit改为junit-no-dep,或者改变类加载的搜索顺寻。

 

要测试的方法为Publisher的publish方法

 

 

 

我们可以使用一个实现好的Subscriber,在publish之后,通过Subscriber的行为来判断msg是否receive。显然,如引言中所述,引入Subscriber的依赖,单元测试的边界变得模糊,我们到底是测试publish呢,还是测试receive呢?

 

jmock 测试code如下:

 

 

1. 生成要测试的object,Publisher为真publisher, Subscriber则是mock生成。

2. 让target对象使用mock对象,或者将mock对象设置到测试的target对象中

3. 声明mock对象的expectations,就是mock的哪些方法被调用,调用的输入输出是什么。例子中,声明subscriber的receive被调用一次,输入参数是message,没有输出。

4. 运行要测试的方法

5. 进行verify, context.assertIsSatisfied(),来assert调用是否和expectation一致。在实际测试中,我们往往加上自己的assert,assert测试方法的return(本例中target方法也没有输出)

 

从上面可以看出,使用jmock的一个关键部分就是声明expectations。

Expectations

expectations的语法如下:
invocation-count (mock-object).method(argument-constraints);
    inSequence(sequence-name);
    when(state-machine.is(state-name));
    will(action);
    then(state-machine.is(new-state-name));
看起来似乎很多不?除了 invocation count 和mock object外,其他都是optional.  其核心无非就是declare方法的调用,其他都浮云。
(mock-object).method(argument-constraints)  will(action);
基本对应以下java语法
Object xx = mock.method(parameter); 
return xxx;

 

 

具体每个部分说明如下:

Invocation count

方法触发的次数,使用较多的是

oneOf 触发一次

exactly(n).of 触发n次

allowing 任意次

Argument Matchers

方法参数的匹配,使用with声明,主要使用same, equal, any等

 

oneOf (mock).doSomething(with(equal(1))); //参数为单一参数,值equal1

oneOf (mock).doSomething(with(same(1)))  // 参数为单一参数,直 == 1

oneOf (mock).doSomething(with(any(Class<T> type))) //参数为单一参数,只要是class类型即可。

 

Actions

method invocation后的,使用will声明,主要使用return和throwException等

 

will(returnValue(v))

will(throwException(e))

States

状态机都出来了。哥还真用了一把。在最先的版本中,我们使用Spring来注入Mockery,所有test共用了一个Mockery,这样各个unit test的Expecting相互影响。怎么隔离开来呢?加上state condition

 

  • 在每个测试的开头声明一个States

final States pen = context.states("mymethod").startsAs("start");

  • 在expecting的method invocation加入when

 

 

oneOf (turtle).forward(10); when(pen.is("start"));
  • 在测试结束时,将状态机的状态设置为非start的其他状态

 

Sequences

invocation 的顺序,语法类似于States

Invocations that are expected in a sequence must occur in the order in which they appear in the test code. A test can create more than one sequence and an expectation can be part of more than once sequence at a time.

Mocking class instead of interface

Add jmock-legacy-2.5.1.jar, cglib-nodep-2.1_3.jar and objenesis-1.0.jar to your CLASSPATH. 连final class夜可以被mock,引入jdave即可

 

 

Annotation

自动生成mock对象

 

自动生成States/Sequence
@Auto States pen;
@Auto Sequence events;

 

The limits

  • closely couple: unit test 和方法的实现紧耦合。这样如果你的方法或者类refactor,unit test要做相应的修改
  • 当mock object数量很多,维护的成本相应增加
  • mock的行为如果不正确,有可能导致unit test变成non-sense,检测不出bug

 

由于我们系统的domain model很复杂(遗留原因),这时引入jmock,我们发现生成mock对象的过程,似乎是将所有Domain object和implementation又重新写了一遍。所有上述limit一下子暴露无遗。

 

目前暂时没有想到有效的应对下述系统的测试方法:

1.  db design is complex/legacy

2.  domain model (wsdl/xsd) design is complex. because we reuse the old business-rules, while those business-rules is not examine and verify carefully in the past.

 

一个遗留的db加上一个复杂的domain model,虽然business logic很简单,没有任何复杂的商业计算,只是简单的DAO -> WSDL Bean的操作,实现起来让开发人员觉得纷繁芜杂,难以入手。

 

在本文完成之时,我们决定仍然使用Jmock, 效果如何,下回分解。

关于DoubleBrace

 

 

这是一个trick,DoubleBraceInitialization

 

The first brace creates a new AnonymousInnerClass, the second declares an instance initializer block that is run when the anonymous inner class is instantiated

 

第一个大括号声明了一个Annoymous的InnerClass,例子中声明了一个Hashset的子类;第二个括号则是 instance initializer block,实例的初始化模块。expectations的声明相当明了。

 

修订

使用mock,除了前文所说的isolate(隔离)外,模拟(simulate)也是另外一个重要原因,比如浏览器表单提交。。。

更多

参见cookbook

 

http://www.jmock.org/cookbook.html

 

关于更多的java Idioms

http://www.c2.com/cgi/wiki?JavaIdioms

 

 

相关文章推荐

EasyMock与JMock的比较

MockObjects的选择:EasyMock与JMock的比较 本文假设读者已经了解了MockObjects的使用目的和基本方式,不对MockTest之类的技术作过多解释。仅提醒一句:“不要测试你...

jmock-2.6.0-jars

  • 2016年10月25日 22:02
  • 1.41MB
  • 下载

JMock学习文档分享

  • 2013年12月15日 21:36
  • 20KB
  • 下载

Jmock学习文档

JMock是帮助创建mock对象的工具,它基于Java开发,在Java测试与开发环境中有不可比拟的优势,更重要的是,它大大简化了虚拟对象的使用。本文中,通过一个简单的测试用例来说明JMock如何帮助我...

jmock-cglib-1.2.0.jar

  • 2011年07月11日 15:30
  • 4KB
  • 下载

JMOCK使用文档

  • 2013年11月25日 22:46
  • 2.14MB
  • 下载

JUnit4 与 JMock 之双剑合璧

王 建军, 实习生, IBM 简介: 在 developerWorks 上面有一些关于 JUnit4 的文章,也有关于 JMock 的文章,但是结合这两项技术的文章基本上没有。本文就基于这两种技术,...

jmock2.5基本教程 --转载

jmock2.5基本教程目录 第0章 概述第1章 jmock初体验第2章 期望第3章 返回值第4章 参数匹配第5章 指定方法调用次数第6章 指定执行序列第7章 状态机第0章 概述现在的dev不是仅仅要...
  • het9527
  • het9527
  • 2011年03月23日 20:59
  • 1892

简单示例mock的使用 (Junit结合Jmock)

测试数据库时非常脆弱:数据的改变可能会影响测试的结果。  为目标的依赖创建桩(stub)或mock类是更好的解决方案。  可以使用jMock开源库来快速、简单的定义mock对象。(详细请查看:ht...

jmock2.5基本教程

jmock2.5基本教程 目录 第0章 概述 第1章 jmock初体验 第2章 期望 第3章 返回值 第4章 参数匹配 第5章 指定方法调用次数 第6章 指定执行序列 第7章 状...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Jmock
举报原因:
原因补充:

(最多只允许输入30个字)