声明:
Author:赵志乾
Date:2017-8-6
Declaration:All Right Reserved!!!
1、在给定的测试中,一个expectation代表对一个特定的、被mocked的方法/构造函数的一组调用。它可能会涵盖测试过程中该方法的多个不同的调用,但这并非强制要求。而方法的一次调用是否匹配特定的expectation,不仅取决于方法签名,还包括传入参数、以及运行时已经匹配的调用次数。我们可以通过指定特定的参数值,使得特定的函数调用匹配特定的expectation。当然,也可以通过指定比较宽泛的匹配条件,使得同一方法的一组调用均能匹配该expectation。下面给出一个示例:
@Test public void doBusinessOperationXyz(@Mocked final Dependency mockInstance) { ... new Expectations() {{ ... // An expectation for an instance method: mockInstance.someMethod(1, "test"); result = "mocked"; ... }}; // A call to code under test occurs here, leading to mock invocations // that may or may not match specified expectations. }
该示例给出的是Dependency#someMethod(int, String)方法的expectation,并且需要通过特定的参数匹配方法调用。
2、录制、重放、验证三阶段
测试过程通常可以划分出三个顺序执行的阶段,如下例所示:
@Test public void someTestMethod() { // 1. Preparation: whatever is required before the code under test can be exercised. ... // 2. The code under test is exercised, usually by calling a public method. ... // 3. Verification: whatever needs to be checked to make sure the code exercised by // the test did its job. ... }
首先是准备阶段,在该阶段主要是创建/获取测试过程中需要的对象和数据项。之后是在测试条件下执行代码。最后是结果的验证,即将执行结果和预期结果进行对比。而在基于行为测试(即包含mocked类型)的上下文中,我们通常会将测试过程划分为下面的三个阶段。
首先是录制阶段,其主要是在mocked方法被调之前对其调用行为进行录制,即完成测试的准备工作。其次是重放阶段,该阶段是指代码执行过程中,如果mocked的方法被调用,将会重放前一阶段录制的行为,即mocked方法表现出预期的行为。最后是验证阶段,该阶段是在测试验证过程中发生,主要是验证mocked方法的调用和我们预期的是否一致。下面给出了使用JMockit的基于行为的测试模板:
import mockit.*; ... other imports ... public class SomeTest { // Zero or more "mock fields" common to all test methods in the class: @Mocked Collaborator mockCollaborator; @Mocked AnotherDependency anotherDependency; ... @Test public void testWithRecordAndReplayOnly(mock parameters) { // Preparation code not specific to JMockit, if any. new Expectations() {{ // an "expectation block" // One or more invocations to mocked types, causing expectations to be recorded. // Invocations to non-mocked types are also allowed anywhere inside this block // (though not recommended). }}; // Unit under test is exercised. // Verification code (JUnit/TestNG assertions), 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 // (though not recommended). }}; // 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 Expectations() {{ // One or more invocations to mocked types, causing expectations to be recorded. }}; // Unit under test is exercised. new VerificationsInOrder() {{ // an ordered 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. } }
注意:一个测试可以包含任意数量的expectation块,甚至可以不包含。
事实上,匿名内部类可以定界代码块,这可以让我们充分利用IDE中代码折叠的特性,如下面的这张图片所示:
注:本博客中的实例代码均来自于JMockit官方教程。