下图为jmockit 类图。在我们编写代码时几乎都会用到Expectations(期望)和Verifications(校验),二者均继承自Invacations.
常会用到的注解有:@Mocked @Tested @Injectable(@Tested和@Injectable经常配对使用),@Capturing(用于接口)
mock类型和实例
从依赖的测试代码调用的方法和构造函数是mock(模拟)的目标。 Mocking提供了我们需要的机制,以便将被测试的代码与(一些)依赖关系隔离开来。我们通过声明适当的模拟字段和/或模拟参数来指定要为给定测试(或多个测试)模拟哪些特定依赖性; mock字段声明为测试类的注释实例字段,而mock参数声明为测试方法的注释参数。 要模拟的依赖关系的类型将是模拟字段或参数的类型。这种类型可以是任何类型的引用类型:接口,类(包括抽象和final类型),注释或枚举。
默认情况下,mock类型的所有非私有方法(包括静态,final或native的任何方法)将在测试期间被模拟。如果声明的mocked类型是一个类,那么所有的超类直到但不包括java.lang.Object也将被递归地mock。同时,继承的方法也会自动被mock。在一个类的情况下,它的所有非私有构造函数将被mock。
当一个方法或构造器被mock时,它的原始实现代码将不会被执行在测试期间发生的调用。相反,调用将被重定向到JMockit,所以它可以以显式或隐式指定测试方式处理。
以下示例测试框架用作对mock字段和mock参数的声明以及它们在测试代码中通常使用的方式的基本说明。
对于在测试方法中声明的mock参数,声明类型的实例将由JMockit自动创建,并在JUnit / TestNG测试运行器执行测试方法时传递; 因此,参数值永远不会为null。对于mock属性,声明类型的实例将由JMockit自动创建并分配给字段,前提是它不是final(final不允许继承)。
有一些不同的注解可用于声明模拟属性和参数,以及默认模拟行为可以修改以适应特定测试的需要的方式。本章的其他部分详细介绍,但基本是:@Mocked是*模拟注解,有一个可选的属性,在某些情况下是有用的; @Injectable是另一个模拟注释,它限制mock单个模拟实例的实例方法;@Capturing是另一个模拟注释,它扩展mock到实现模拟接口的类,或扩展mock类的子类。当@Injectable或@Capturing应用于模拟字段或模拟参数时,隐含了@Mocked,所以它不需要(但可以)应用。由JMockit创建的模拟实例可以在测试代码(用于期望的记录和验证)中正常使用,和/或传递到测试中的代码。或者他们可能只是闲置。与其他模拟API不同,这些模拟对象不一定是被测试代码在其依赖项上调用实例方法时使用的对象。默认情况下(即,当不使用@Injectable时),JMockit不关心调用模拟实例方法的对象。这允许直接在测试下的代码中创建的实例的透明模拟,当所述代码使用新运算符调用全新实例上的构造函数时;实例化的类必须由测试代码中声明的模拟类型覆盖,这就是全部。
Expectations
期望表示对与给定测试相关的特定模拟方法/构造函数的调用的集合。期望可以覆盖对同一方法或构造器的多个不同调用,但是它不必覆盖在测试的执行期间发生的所有这样的调用。特定调用是否与给定期望匹配将不仅取决于方法/构造函数签名,而且还取决于诸如调用该方法的实例,参数值和/或已经匹配的调用的数量的运行时方面。因此,可以(可选地)为给定期望指定几种类型的匹配约束。
当我们具有一个或多个调用参数时,可以为每个参数指定确切的参数值。例如,可以为String参数指定值“test string”,从而导致期望仅在相应参数中将这些调用与此精确值进行匹配。正如我们将在后面看到的,我们可以指定更宽松的约束,而不是指定精确的参数值,它将匹配整组不同的参数值。
下面的示例显示了Dependency#someMethod(int,String)的期望,它将使用指定的确切参数值匹配此方法的调用。注意,期望本身是通过对模拟方法的单独调用来指定的。没有涉及特殊的API方法,这在其他mocking API中是常见的。然而,这种调用不算作我们对测试感兴趣的“真正的”调用之一。它只在那里,以便可以指定期望。
@Test
public void doBusinessOperationXyz(@Mocked final Dependency mockInstanc){
... ...
new Expectations(){{
... ...
//实例方法的期望:
//mockInstance.someMethod(1,“test”);
result =“mocked”;
... ...
}};
//这里调用被测代码,导致模拟调用可能或可能不匹配指定的期望。
}}
在我们了解记录,重放和验证调用之间的差异后,我们将更多地了解期望。
record-replay-verify模型
任何开发人员测试都可以分为至少三个单独的执行阶段。 这些阶段按顺序执行,一次一个,如下所示。
@Test
public void someTestMethod(){
//1.准备:在被测试代码可以执行之前的任何需要。
... ...
// 2.通过调用public方法来执行测试下的代码。
... ...
// 3.验证:无论需要检查以确保代码行使
// 测试完成了它的工作。
... ...
}}
import mockit。*;
...other import...
public class SomeTest{
//零或多个“模拟字段”对类中的所有测试方法是通用的:
@Mocked Collaborator mockCollaborator;
@Mocked AnotherDependency anotherDependency;
... ...
// 第一种情况:
@Test
public void testWithRecordAndReplayOnly(mock parameters) {
//准备代码不特定于JMockit,可以使任意的。
new Expectations(){{
//“expectation block”
//对模拟类型的一个或多个调用,导致期望被记录。
//在这个块内的任何地方也允许调用非模拟类型
//(虽然不推荐)。
}};
//执行被测单元。
//验证代码码(JUnit/TestNG assertions),如果有的话。
}}
// 第二种情况:
@Test
public void testWithReplayAndVerifyOnly(mock parameters) {
准备代码不特定于JMockit,可以使任意的。
执行被测单元。
new Verifications() {{
//“验证块”
//对模拟类型的一个或多个调用,导致期望被验证。
//在这个块内的任何地方也允许调用非模拟类型
//(虽然不推荐)。
}};
//附加验证码(如果有的话),位于验证区块之前或之前。
}}
// 第三种情况:
@Test
public void testWithBothRecordAndVerify(mock parameters) {
//准备代码不特定于JMockit,如果有的话。
//new Expectations(){{
////对模拟类型的一个或多个调用,导致期望被记录。
}};
//执行被测单元。
new VerificationsInOrder(){{
//有序的验证块
//对模拟类型的一个或多个调用,导致期望被验证
//以指定的顺序。
}};
//附加验证码(如果有的话),位于验证区块之前或之前。
}}
}}
上述模板还有其他变化,但本质是,期望块属于记录阶段,并且在被测试代码执行之前,而验证块属于验证阶段。 测试方法可以包含任何数量的期望块,也可以没有。 验证块也是如此。事实上,匿名内部类用于区分代码块,这使我们可以利用现代Java IDE中提供的“代码折叠”功能。 下图显示了IntelliJ IDEA中的内容。
定期与严格的期望
严格和非严格的模拟
注意,我们不指定给定的模拟类型/实例应该是严格的或不。相反,给定模拟场/参数的严格性由如何在测试中使用来确定。一旦在“新的StrictExpectations(){...}”块中记录了第一个严格的期望,相关的模拟类型/实例被认为对于整个测试是严格的;否则,就不会严格。
记录期望的结果
public class UnitUnderTest{
(1)private final DependencyAbc abc = new DependencyAbc();
public void doSomething(){
(2)int n = abc.intReturningMethod();
for(int i = 0; i <n; i ++){
String s;
catch{
(3)s = abc.stringReturningMethod();
}}
catch(SomeCheckedException e){
//以某种方式处理异常
}}
//做一些其他的东西
}}
}}
}}
// doSomething()方法的一个可能的测试可以运行在任意数量的成功迭代之后抛出SomeCheckedException的情况。假设我们想要(为了什么原因)记录这两个类之间的交互的一整套期望,我们可以写下面的测试。 (通常,指定对模拟方法的所有调用和特定的在给定测试中的模拟构造函数是不可取的或重要的,我们稍后将解决这个问题。
@Test
public void doSomethingHandlesSomeCheckedException(@Mocked final DependencyAbc abc)throws Exception{
new Expectations(){{
(1)new DependencyAbc();
(2)abc.intReturningMethod();
result = 3;
(3)abc.stringReturningMethod();
returns(“str1”,“str2”);
result = new SomeCheckedException();
}};
new UnitUnderTest().doSomething();
}}
匹配调用特定实例(@Injectable)
可注入的模拟实例
在线工具 保存(Save) 嵌入博客(Embed) 执行(Run) 1 public final class ConcatenatingInputStream extends InputStream{
2
private final Queue <InputStream> sequentialInputs;
3
private InputStream currentInput;
4 public ConcatenatingInputStream(InputStream ... sequentialInputs){
5
this.sequentialInputs = new LinkedList <InputStream>(Arrays.asList(sequentialInputs));
6
currentInput = this.sequentialInputs.poll();
7
}}
8
@Override
9 public int read()throws IOException{
10
if(currentInput == null)return -1;
11
int nextByte = currentInput.read();
12 if(nextByte> = 0){
13
return nextByte;
14
}}
15
currentInput = sequentialInputs.poll();
16
return read();
17
}}
18
}}
19
// 这个类可以通过使用ByteArrayInputStream对象进行输入而轻松地进行测试,但是我们希望确保InputStream#read()方法在构造函数中传递的每个输入流上被正确调用。以下测试将实现这一点。
20
@Test
21 public void concatenateInputStreams(@Injectable final InputStream input1,@Injectable final InputStream input2)throws Exception{
22 new Expectations(){{
23
input1.read();returns(1,2,-1);
24
input2.read();returns(3,-1); }};
25
InputStream concatenatedInput = new ConcatenatingInputStream(input1,input2);
26
byte [] buf = new byte [3];
27
concatenatedInput.read(buf);
28
assertArrayEquals(new byte [] {1,2,3},buf);
29
}} 缩进 减少缩进 注释 格式化 227310278
小子欠扁 返回顶部
声明多个mock实例
@Test
public void matchOnMockInstance(@Mocked final Collaborator mock,@Mocked Collaborator otherInstance){
new Expectations(){{
mock.getValue(); result = 12;
}};
//使用从测试传递的模拟实例练习测试中的代码:
int result = mock.getValue();
assertEquals(12,result);
//如果另一个实例在测试下的代码中创建...
//Collaborator another = new Collaborator();
// ...我们不会得到记录的结果,但默认的一个:
assertEquals(0,another.getValue());
}}
使用给定构造函数创建的实例
@Test
public void newCollaboratorsWithDifferentBehaviors(@Mocked Collaborator anyCollaborator){
//记录每组实例的不同行为:
new Expectations(){{
//一组,使用“a value”创建的实例:
Collaborator col1 = new Collaborator(“a value”);
col1.doSomething(anyInt); result = 123;
//另一个集合,使用“另一个值”创建的实例:
Collaborator col2 = new Collaborator("another value");
col2.doSomething(anyInt); result = new InvalidStateException();
}};
//测试代码:
new Collaborator(“a value”).doSomething(5);
//将返回123
... ...
new Collaborator(“another value”).doSomething(0);
//会抛出异常
... ...
}}
@Test
public void newCollaboratorsWithDifferentBehaviors(@Mocked final Collaborator col1, @Mocked final Collaborator col2){
new Expectations(){{
将单独的未来实例集映射到单独的模拟参数:
new Collaborator("a value"); result = col1;
new Collaborator("another value"); result = col2;
//记录每组实例的不同行为:
col1.doSomething(anyInt); result = 123;
col2.doSomething(anyInt); result = new InvalidStateException();
}};
//测试代码:
new Collaborator("a value").doSomething(5);
//将返回123
... ...
new Collaborator("another value").doSomething(0);
//会抛出异常
... ...
}}
参数值的灵活匹配
使用“any”字段进行参数匹配
@Test
public void someTestMethod(@Mocked final DependencyAbc abc){
final DataItem item = new DataItem(...);
new Expectations(){{
将匹配第一个参数为“voidMethod(String,List)”调用
//任何字符串和第二个任何列表。
abc.voidMethod(anyString,(List <?>)any);
}};
new UnitUnderTest().doSomething(item);
new Verifications() {{
//匹配具有long或Long类型的任何值的指定方法的调用。
abc.anotherVoidMethod(anyLong);
}};
}}
使用“with”方法进行参数匹配
@Test
public void someTestMethod(@Mocked final DependencyAbc abc){
final DataItem item = new DataItem(...);
new Expectations(){{
将匹配“voidMethod(String,List)”调用第一个参数
//等于“str”,第二个不为null。
abc.voidMethod(“str”,(List <?>)withNotNull());
//将匹配调用到DependencyAbc#stringReturningMethod(DataItem,String)
//第一个参数指向“item”,第二个参数指向“xyz”。
abc.stringReturningMethod(withSameInstance(item),withSubstring(“xyz”));
}};
new UnitUnderTest().doSomething(item);
new Verifications() {{
//使用任何长整数参数匹配指定方法的调用。
abc.anotherVoidMethod(withAny(1L));
}};
}}
使用空值匹配任何对象引用
@Test
public void someTestMethod(@Mocked final DependencyAbc abc){
... ...
new Expectations(){{
abc.voidMethod(anyString,null);
}};
... ...
}}
通过varargs参数传递的匹配值
指定调用计数约束
@Test
public void someTestMethod(@Mocked final DependencyAbc abc){
new Expectations(){{
默认情况下,至少需要一次调用,即“minTimes = 1”:
new DependencyAbc();
//至少需要两次调用:
abc.voidMethod(); minTimes = 2;
//需要1到5次调用:
abc.stringReturningMethod();
minTimes = 1;
maxTimes = 5;
}};
new UnitUnderTest().doSomething();
}}
@Test
public void someOtherTestMethod(@Mocked final DependencyAbc abc){
new UnitUnderTest().doSomething();
new Verifications() {{
验证发生了零个或一个调用,并指定了参数值:
abc.anotherVoidMethod(3);
maxTimes = 1;
//使用指定的参数验证至少一次调用的发生:
DependencyAbc.someStaticMethod(“test”,false);
//“minTimes = 1”
}};
}}
显示验证
@Test
public void verifyInvocationsExplicitlyAtEndOfTest(@Mocked final Dependency mock){
//这里没有记录,虽然它可以。
// Inside tested code:
Dependency dependency = new Dependency();
dependency.doSomething(123, true, "abc-xyz");
//验证Dependency#doSomething(int,boolean,String)被调用至少一次,
//具有遵守指定约束的参数:
new Verifications(){{
mock.doSomething(anyInt,true,withPrefix(“abc”));
}};
}}
验证调用从未发生
验证按顺序
@Test
public void validationExpectationsInOrder(@Mocked final DependencyAbc abc){
//里面的测试代码:
abc.aMethod();
abc.doSomething(“blah”,123);
abc.anotherMethod(5);
... ...
new VerificationsInOrder(){{
//这些调用的顺序必须与顺序相同
//重放匹配调用期间的发生。
abc.aMethod();
abc.anotherMethod(anyInt);
}};
}}
部分有序验证
jmockit学习的更多相关文章
-
jmockit学习总结
mock类型和实例 从依赖的测试代码调用的方法和构造函数是mock(模拟)的目标. Mocking提供了我们需要的机制,以便将被测试的代码与(一些)依赖关系隔离开来.我们通过声明适当的模拟字段和/或模 ...
-
JMockit学习笔记
一.在eclipse下建立JMockit工程 1.下载最新版JMockit(jmockit-1.4.zip): 2.解压后的文件夹包含有:library jars, source files, sam ...
-
java 学习之路
一.基础篇 1.1 JVM 1.1.1. Java内存模型,Java内存管理,Java堆和栈,垃圾回收 http://www.jcp.org/en/jsr/detail?id=133 http://i ...
-
JUnit与JMock学习
JUnit与JMock学习 测试驱动编程和持续集成部署应该说是现在软件开发者的必备武器,不过跟其他很多好东西一样,在我们公司的推广总要慢上一拍,毕竟老板看的是你能够把功能实现好让客户满意,所以能不折腾 ...
-
Java学习路线(转)
原文:http://www.hollischuang.com/archives/489 一.基础篇 1.1 JVM 1.1.1. Java内存模型,Java内存管理,Java堆和栈,垃圾回收 http ...
-
如何学习Java?
一点感悟 java作为一门编程语言,在各类编程语言中作为弄潮儿始终排在前三的位置,这充分肯定了java语言的魅力,在实际项目应用中,我们已经无法脱离javaa(Ps当然你可以选择不使用),但它的高性能 ...
-
从直播编程到直播教育:LiveEdu.tv开启多元化的在线学习直播时代
2015年9月,一个叫Livecoding.tv的网站在互联网上引起了编程界的注意.缘于Pingwest品玩的一位编辑在上网时无意中发现了这个网站,并写了一篇文章<一个比直播睡觉更奇怪的网站:直 ...
-
Angular2学习笔记(1)
Angular2学习笔记(1) 1. 写在前面 之前基于Electron写过一个Markdown编辑器.就其功能而言,主要功能已经实现,一些小的不影响使用的功能由于时间关系还没有完成:但就代码而言,之 ...
-
ABP入门系列(1)——学习Abp框架之实操演练
作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...
随机推荐
-
初识Azkaban
先说下hadoop 内置工作流的不足 (1)支持job单一 (2)硬编码 (3)无可视化 (4)无调度机制 (5)无容错机制 在这种情况下Azkaban就出现了 1)Azkaban是什么 Azkaba ...
-
【工业串口和网络软件通讯平台(SuperIO)教程】二.架构和组成部分
1.1 架构结构图 1.1.1 层次示意图 1.1.2 模型对象示意图 1.2 IO管理器 IO管理器是对串口和网络通讯链路的管理.调度.针对串口和网络通讯链路的特点,在IO管 ...
-
UITableViewCell里面separator的设置
最近cell显示的时候左边一直有15个像素的偏移,查了下面的方法 //1. 不管用 [self.tableView setSeparatorInset:UIEdgeInsetsZero]; // 2. ...
-
Winform开发框架的重要特性总结
从事Winform开发框架的研究和推广,也做了有几个年头了,从最初的项目雏形到目前各种重要特性的加入完善,是经过了很多项目的总结归纳和升华,有些则是根据客户需要或者应用前景的需要进行的完善,整个Win ...
-
Qt postEvent
Qt3中可以直接向线程发送消息 QThread::postEventQ4中已不支持为了模拟向线程发送消息,可以通过QObject::moveToThread后,然后再向这个QObject发送消息 ob ...
-
gps 经纬度 转换实际距离
<!doctype html> <html lang="zh-CN"> <head> <meta charset="utf-8& ...
-
python学习日记(内置函数补充)
剩余匿名函数 序列 序列——列表和元组相关的:list和tuple 序列——字符串相关的:str,format,bytes,bytearry,memoryview,ord,chr,ascii,repr ...
-
MySQL 并行复制(MTS) 从库更新的记录不存在实际却存在
目录 背景 版本 分析 测试 背景 开了并行复制的半同步从库SQL 线程报1032错误,异步复制从库没有报错,偶尔会出现这种 版本 mysql 5.7.16 redhat 6.8 mysql> ...
-
canvas版《俄罗斯方块》
试玩(没有考虑兼容低版本浏览器): See the Pen Canvas俄罗斯方块 by 王美建 (@wangmeijian) on CodePen. ************************ ...
-
CLOB 和 BLOB
An SQL CLOB is a built-in type that stores a Character Large Object as a column value in a row of a ...