JMockit简介
在JMockit工具包中,Expectations &Verifications APIs为behavior-based单元测试的创建提供了丰富的支持。这种测试的关注点在于测试单元和其他相关联的测试单元的交互作用。测试单元包括class,method和constructor。
两个单元之间的交互通常表现为method或constructor之间的调用。
一个单元测试通常只运用被测试的这一个单元。其背后依赖的一些其他单元并不需要被运用到。因此,单元测试的目的是测试该单元的内部运行逻辑,测试时应该与其依赖的其他单元相孤立。
但是我们在测试时并不是要与所有的依赖单元都孤立开来,通常只对以下的情况做孤立。
1) 已经有(或这将要)自己的单元测试。
2) 由于某些原因导致在测试环境中很难被创建或运行。
对于上述的这些特殊的单元,我们假定这些依赖的行为都是按照预期(expectations)执行的。
Mocked types
在测试单元中被调用的方法或构造函数,以及被依赖到的单元通常都是模拟的对象。Mocking提供了一种机制,使得那些被测试的单元可以与他依赖的单元孤立开来。我们声明某个对象为mocked类型来指定在本测试中该依赖的对象是模拟的。能被模拟的类型为:interface,class(包括abstract和final类型的),annotation和enum。
在默认情况下,被模拟的类型中都有的方法都是被模拟的。如果声明某个class为模拟的,则它的所有直到java.lang.Object(但不包含java.lang.Object)的父类都是被模拟的。因此继承的方法也都是自动被模拟的。另外在class中都有的构造函数也会被模拟。不论是private
,static
,final
还是native
的方法/构造函数都能被模拟。
当一个方法或构造函数被模拟后,在测试期间,他原有的实现代码就不会被调用。该方法或构造函数的调用就会被重定向都JMockit中。
以下是一个最基本的例子。
@Test
public void doBusinessOperationXyz()
{
...
new Expectations() { // an "expectation block"
Dependency mockInstance; // "Dependency" is our mocked type for this test
...
{
...
// "mockInstance" is a mocked instance automatically provided for use in the test
mockInstance.mockedMethod(...);
...
}
};
...
}
通常上例中的变量还可以通过@Mocked
,@NonStrict
,@Injectable
这些注释来声明是被模拟的。
Expectations
一个expectation就是一组在测试中对某个特定模拟方法/构造函数的调用。一个expectation可以包含对同一个方法/构造函数的不同调用,但并不需要包含在测试过程中的所有调用。一个调用是否能匹配到某个expectation,不仅仅决定与该法/构造函数的名称,还取决于运行时的参数,如该方法所属对象的实例,参数值,或被调用的次数等。因此在expectation中可以指定一些匹配调用的约束条件。
我们还可以对expectation中被调用的方法的参数做一些限定,从而匹配到特定条件的方法调用。
如下例中为Dependency#someMethod(int, String)
方法的一个
expectation,只有满足该参数值(1,”test”)的方法调用才会匹配到该expectation。
@Test
public void doBusinessOperationXyz()
{
...
new Expectations() {
Dependency mockInstance;
...
{
...
// An expectation for an instance method:
mockInstance.someMethod(1, "test");
...
}
};
// A call to the unit under test occurs here, leading to mock invocations
// that may or may not match specified expectations.
}
The record-replay-verify model
所有的测试都可以至少分割为三个阶段。
如下所示
@Test
public void someTestMethod()
{
// 1. Preparation: whatever is required before the unit under test can be exercised.
...
// 2. The unit under test is exercised, usually by calling a public method.
...
// 3. Verification: whatever needs to be checked to make sure the exercised unit
// did its job.
...
}
首先是准备阶段,在该阶段中测试时所需要的对象或者数据将被创建或重其他地方获取来。
然后测试单元被执行。
最后,将运行结果与期待值进行比较。
这个三阶段模型也成为:Arrange, Act, Assert语法,(简称"AAA")
在使用模拟类型的测试中,我们对该三个阶段定义如下:
1) Record阶段:该阶段中调用将被录制。在测试准备阶段,在被测试单元运行之前进行。
2) Replay阶段:该阶段中在测试单元运行时,模拟的调用将可能会被执行。先前被录制好的对模拟方法的调用将会被回放。通常调用的录制和回放并不一定是一一映射的关系。
3) Verify阶段:该阶段中可以验证调用有没有按照期望运行。在测试验证阶段,调用的方法被执行后运行。
使用JMockit进行的Behavior-based测试可以归纳为以下的模版。
import mockit.*;
... other imports ...
public class SomeTest
{
// Zero or more "mock fields" common to all test methods in the class:
@Mocked Collaborator mockCollaborator;
@NonStrict AnotherDependency anotherDependency;
...
@Test
public void testWithRecordAndReplayOnly(mock parameters)
{
// Preparation code not specific to JMockit, if any.
new Expectations() { // an "expectation block"
// Zero or more local mock fields.
{
// One or more invocations to mocked types, causing expectations to be recorded.
// Invocations to non-mocked types are also allowed anywhere inside this block.
}
};
// Unit under test is exercised.
// Verification code (JUnit/TestNG asserts), if any.
}
@Test
public void testWithReplayAndVerifyOnly(mock parameters)
{
// Preparation code not specific to JMockit, if any.
// Unit under test is exercised.
new Verifications() {{ // a "verification block"
// One or more invocations to mocked types, causing expectations to be verified.
// Invocations to non-mocked types are also allowed anywhere inside this block.
}};
// Additional verification code, if any, either here or before the verification block.
}
@Test
public void testWithBothRecordAndVerify(mock parameters)
{
// Preparation code not specific to JMockit, if any.
new NonStrictExpectations() { // also an expectation block
// Zero or more mock fields.
{
// One or more invocations to mocked types, causing expectations to be recorded.
}
};
// Unit under test is exercised.
new VerificationsInOrder() {{ // also a verification block
// One or more invocations to mocked types, causing expectations to be verified
// in the specified order.
}};
// Additional verification code, if any, either here or before the verification block.
}
}