Mockito 库、powermock扩展
转载:http://blog.csdn.net/kittyboy0001/article/details/18709685
Mockito 简介
Mockito 是目前 java 单测中使用比较流行的 mock 工具。其他还有 EasyMock,JMock,MockCreator,Mockrunner,MockMaker 及 PowerMock。
获取地址:https://code.google.com/p/mockito/
powermock 简介
EasyMock 以及 Mockito 都因为可以极大地简化单元测试的书写过程而被许多人应用在自己的工作中,但是这两种 Mock 工具都不可以实现对静态函数、构造函数、私有函数、Final 函数以及系统函数的模拟,但是这些方法往往是我们在大型系统中需要的功能。PowerMock 是在 EasyMock 以及 Mockito 基础上的扩展,通过定制类加载器等技术,PowerMock 实现了之前提到的所有模拟功能,使其成为大型系统上单元测试中的必备工具。缺点是缺少文档。
获取地址:https://code.google.com/p/powermock/downloads/list
Mock 和Stub
Mock
所谓的mock,即模拟,模仿的意思。Mock 技术的主要作用是使用mock工具模拟一些在应用中不容易构造或者比较复杂的对象,从而把测试目标与测试边界以外的对象隔离开。
Stub
Stub,桩。单元测试过程中,对于在应用中不容易构造或者比较复杂的对象,用一个虚拟的对象来代替它。从类的实现方式上看,stub有一个显式的类实现,按照stub类的复用层次可以实现为普通类(被多个测试案例复用),内部类(被同一个测试案例的多个测试方法复用)乃至内部匿名类(只用于当前测试方法)。stub的方法也会有具体的实现,哪怕简单到只有一个简单的return语句。
Stub 与 Mock 的区别
Stub 是在单元测试过程中去代替某些对象来提供所需的测试数据,适用于基于状态的(state-based)测试,关注的是输入和输出。而Mock适用于基于交互的(interaction-based)测试,关注的是交互过程,不只是模拟状态,还能够模拟模块或对象的行为逻辑并能验证其正确性,Mock不需要类的显示实现,直接用工具模拟。
Mockito 的使用
Maven
通过Maven管理的,需要在项目的Pom.xml中增加如下的依赖:
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
</dependencies>
在程序中可以import org.mockito.Mockito,然后调用它的static方法。
Maven 程序
1.模拟对象
创建 Mock 对象的语法为 mock(class or interface)。
2.设置对象调用的预期返回值
通过 when(mock.someMethod()).thenReturn(value) 来设定 Mock 对象某个方法调用时的返回值。或者使用 when(mock.someMethod()).thenThrow(new RuntimeException) 的方式来设定当调用某个方法时抛出的异常。
3.验证被测试类方法
Mock 对象一旦建立便会自动记录自己的交互行为,所以我们可以有选择的对它的 交互行为进行验证。在 Mockito 中验证 Mock 对象交互行为的方法是 verify(mock).someMethod(…)。最后 Assert() 验证返回值是否和预期一样。
Demo
Mock 对象的创建
mock(Class<T> classToMock);
mock(Class<T> classToMock, String name)
mock(Class<T> classToMock, Answer defaultAnswer)
mock(Class<T> classToMock, MockSettings mockSettings)
mock(Class<T> classToMock, ReturnValues returnValues)
可以对类和接口进行mock对象的创建,创建时可以为mock对象命名。对mock对象命名的好处是调试的时候容易辨认mock对象。
Mock对象的期望行为和返回值设定
假设我们创建了LinkedList类的mock对象:
LinkedList mockedList = mock(LinkedList.class);
对方法进行设定返回值和异常
对包含返回值的方法的设定
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
Mockito支持迭代风格的返回值设定
when(mockedList.get(anyInt()).thenReturn("first").thenThrow(new RuntimeException());
when(mockedList.get(anyInt()).thenReturn("first","second");
Stubbing的另一种风格
doReturn("Hello").when(mockedList).get(0);
doReturn("Hello").doReturn("world").when(mockedList).get(anyInt());
抛出异常
doThrow(new RuntimeException()).when(mockedList).get(0);
对void方法进行方法预期设定
doNothing().when(mockedClass).SomeVoidMethod();
doThrow(new RuntimeException()).when(mockedClass).SomeVoidMethod();
迭代风格
doNothing().doThrow(new RuntimeException()).when(mockedClass).SomeVoidMethod();
参数匹配器
在Stubbing和Verify的时候,有时需要更加灵活的参数需求。参数匹配器(Argument Matcher)能够满足需求。
//stubbing using anyInt() argument matcher
when(mockedList.get(anyInt())).thenReturn("element");
//following prints "element"
System.out.println(mockedList.get(999));
//you can also verify using argument matcher
verify(mockedList).get(anyInt());
需要注意的是,如果使用了参数匹配器,所有的参数都需要由匹配器提供。如下eq("third argument"),直接修改为“third argument”会报错。
verify(mockedClass).someMethod(anyObject(), anyString(), eq("third argument"));
Mock对象行为的验证
Mock 对象行为的验证,关注其交互行为,如mock对象调用的参数,调用次数,调用顺序等。
调用次数验证
public static <T> T verify(T mock).someMethod()
public static <T> T verify(T mock, VerificationMode mode).someMethod()
Parameters:
mock - to be verified
mode - times(M), atLeastOnce() , atLeast(N) , atMost(X) , never()
Returns:
mock object itself
调用顺序验证
public static InOrder inOrder(java.lang.Object... mocks)
创建mock对象
// Multiple mocks that must be used in a particular order
List firstMock = mock(List.class);
List secondMock = mock(List.class);
调用mock对象的方法
//using mocks
firstMock.add("was called first");
secondMock.add("was called second");
创建InOrder对象
//create inOrder object passing any mocks that need to be verified in order
InOrder inOrder = inOrder(firstMock, secondMock);
验证方法调用
//following will make sure that firstMock was called before secondMock
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");
RETURN_SMART_NULLS和RETURN_DEEP_STUBS
RETURN_SMART_NULLS 是实现了Answer 接口的对象,它是创建mock对象时的一个可选参数, mock(class,answer)。在创建mock对象时,使用该参数,调用没有stubbed的方法会返回 SmartNull 。如返回类型为String的,会返回空"", int 会返回 0,List 会返回 null。
mock对象使用RETURNDEEPSTUBS 参数,会自动mock该对象中包含的对象。
注解
Mockito支持对变量进行注解,如将mock对象设为测试类的属性,然后通过注解的方式@Mock来定义它,可以减少重复代码,增强可维护性。Mockito支持的注解有@Mock,@Spy,@Captor,@InjectMocks。
只要在被测试类上标记@InjectMocks,Mockito就会自动将标记@Mock、@Spy等注解的属性值注入到被测试类中。
Annotation 的初始化
初始化方法为调用MockitoAnnotations.initMocks(testClass),可以放到@Before中。
public class ArticleManagerTest {
@Mock private ArticleCalculator calculator;
@Mock private ArticleDatabase database;
@Mock private UserProvider userProvider;
@Before public void setup() {
MockitoAnnotations.initMocks(testClass);
}
}
使用Mockito提供的Junit Runner可以省略上述步骤。
@RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
@Mock private List list;
@Test public void shouldDoSomething() {
list.add(100);
}
}
具体的例子:
//被测试对象
@Service
public class DemandService extends AbstractDataBindListener<DemandInvestigation> {
@Autowired
private CaseSN caseSN;
@Autowired
private WorkflowService workflowService;
public DemandInvestigation findDemandByNum(String demandNum) {
return this.commonDao.findUniqueByHql(
" from DemandInvestigation d"+
" where d.num = ?"+
" and d.type='investigative'",
demandNum);
}
}
测试程序如下:
public class DemandServiceTest {
//被测试类
@InjectMocks
DemandService service;
//依赖注入的类
@Mock
private CommonDao commonDao;
@Before
public void init(){
//初始化Mock对象
MockitoAnnotations.initMocks(this);
}
@Test
public void findDemandByNumTest(){
String demandNum = "IDCCG";
DemandInvestigation demand = new DemandInvestigation();
demand.setNum(demandNum);
Mockito.when(commonDao.findUniqueByHql(Mockito.anyString(),Mockito.anyObject())).thenReturn(demand);
DemandInvestigation ans = service.findDemandByNum(demandNum);
assertTrue(ans.getNum().equals(demandNum));
Mockito.verify(commonDao).findUniqueByHql(Mockito.anyString(),Mockito.anyString());
}
}
powermock 的使用
Maven配置
使用Junit 4.4及以上版本,maven的配置如下:
<properties>
<powermock.version>1.5.1</powermock.version>
</properties>
<dependencies>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
使用 JUnit 4.0-4.3的maven配置如下:
<properties>
<powermock.version>1.5.1</powermock.version>
</properties>
<dependencies>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-legacy</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
对于其他版本的Junit,或者是TestNG,请查看链接:Junit--https://code.google.com/p/powermock/wiki/Mockito_maven,testNG--https://code.google.com/p/powermock/wiki/Mockitotestngmaven
PowerMock 在单元测试中的应用
模拟 Static 方法
在任何需要用到 PowerMock 的类开始之前,首先我们要做如下声明:
@RunWith(PowerMockRunner.class)
然后,还需要用注释的形式将需要测试的静态方法提供给 PowerMock:
@PrepareForTest( { YourClassWithEgStaticMethod.class })
然后就可以开始写测试代码:
1,首先,需要有一个含有 static 方法的代码 , 如
public class IdGenerator {
...
public static long generateNewId() {
...
}
...
}
2,然后,在被测代码中,引用了以上方法
public class ClassUnderTest {
...
public void methodToTest() {
..
final long id = IdGenerator.generateNewId();
..
}
...
}
3,为了达到单元测试的目的,需要让静态方法 generateNewId()返回各种值
来达到对被测试方法 methodToTest()的覆盖测试,实现方式如下:
@RunWith(PowerMockRunner.class)
@PrepareForTest(IdGenerator.class)
public class MyTestClass {
@Test
public void demoStaticMethodMocking() throws Exception {
PowerMockito.mockStatic(IdGenerator.class);
when(IdGenerator.generateNewId()).thenReturn(2L);
new ClassUnderTest().methodToTest();
verifyStatic();
IdGenerator.generateNewId();
}
}
模拟构造函数
有时候,能模拟构造函数,从而使被测代码中 new 操作返回的对象可以被随意定制,会很大程度的提高单元测试的效率,考虑如下:
public class DirectoryStructure {
public boolean create(String directoryPath) {
File directory = new File(directoryPath);
if (directory.exists()) {
throw new IllegalArgumentException(
"\"" + directoryPath + "\" already exists.");
}
return directory.mkdirs();
}
}
为了充分测试 create()函数,我们需要被 new 出来的 File 对象返回文件存在和不存在两种结果。在 PowerMock 出现之前,实现这个单元测试的方式通常都会需要在实际的文件系统中去创建对应的路径以及文件。然而,在 PowerMock 的帮助下,本函数的测试可以和实际的文件系统彻底独立开来:使用 PowerMock 来模拟 File 类的构造函数,使其返回指定的模拟 File 对象而不是实际的 File 对象,然后只需要通过修改指定的模拟 File 对象的实现,即可实现对被测试代码的覆盖测试,参考如下:
@RunWith(PowerMockRunner.class)
@PrepareForTest(DirectoryStructure.class)
public class DirectoryStructureTest {
@Test
public void createDirectoryStructureWhenPathDoesntExist()
throws Exception {
final String directoryPath = "mocked path";
File directoryMock = mock(File.class);
//File的初始化函数的mock
whenNew(File.class).withArguments(directoryPath)
.thenReturn(directoryMock);
// Standard expectations
when(directoryMock.exists()).thenReturn(false);
when(directoryMock.mkdirs()).thenReturn(true);
assertTrue(new NewFileExample()
.createDirectoryStructure(directoryPath));
// Optionally verify that a new File was "created".
verifyNew(File.class).withArguments(directoryPath);
}
}
使用 whenNew().withArguments().thenReturn() 语句即可实现对具体类的构造函数的模拟操作。然后对于之前创建的模拟对象 directoryMock使用 When().thenReturn() 语句,即可实现需要的所有功能,从而实现对被测对象的覆盖测试。在本测试中,因为实际的模拟操作是在类 DirectoryStructureTest 中实现,所以需要指定的 @PrepareForTest 对象是 DirectoryStructureTest.class。
模拟私有以及 Final 方法
为了实现对类的私有方法或者是 Final 方法的模拟操作,需要 PowerMock 提供的另外一项技术:局部模拟。
在之前的介绍的模拟操作中,我们总是去模拟一整个类或者对象,然后使用 When().thenReturn()语句去指定其中值得关心的部分函数的返回值,从而达到搭建各种测试环境的目标。对于没有使用 When().thenReturn()方法指定的函数,系统会返回各种类型的默认值。
局部模拟则提供了另外一种方式,在使用局部模拟时,被创建出来的模拟对象依然是原系统对象,虽然可以使用方法 When().thenReturn()来指定某些具体方法的返回值,但是没有被用此函数修改过的函数依然按照系统原始类的方式来执行。
这种局部模拟的方式的强大之处在于,除开一般方法可以使用之外,Final 方法和私有方法一样可以使用。 参考如下所示的被测代码:
public final class PrivatePartialMockingExample {
public String methodToTest() {
return methodToMock("input");
}
private String methodToMock(String input) {
return "REAL VALUE = " + input;
}
}
为了保持单元测试的纯洁性,在测试方法 methodToTest()时,我们不希望受到私有函数 methodToMock()实现的干扰,为了达到这个目的,我们使用刚提到的局部模拟方法来实现 , 实现方式如下:
@RunWith(PowerMockRunner.class)
@PrepareForTest(PrivatePartialMockingExample.class)
public class PrivatePartialMockingExampleTest {
@Test
public void demoPrivateMethodMocking() throws Exception {
final String expected = "TEST VALUE";
final String nameOfMethodToMock = "methodToMock";
final String input = "input";
PrivatePartialMockingExample underTest = spy(new PrivatePartialMockingExample());
/*
* Setup the expectation to the private method using the method name
*/
when(underTest, nameOfMethodToMock, input).thenReturn(expected);
assertEquals(expected, underTest.methodToTest());
// Optionally verify that the private method was actually called
verifyPrivate(underTest).invoke(nameOfMethodToMock, input);
}
}
可以发现,为了实现局部模拟操作,用来创建模拟对象的函数从 mock() 变成了 spy(),操作对象也从类本身变成了一个具体的对象。同时,When() 函数也使用了不同的版本:在模拟私有方法或者是 Final 方法时,When() 函数需要依次指定模拟对象、被指定的函数名字以及针对该函数的输入参数列表。
Mockito 库、powermock扩展的更多相关文章
- 单元测试及框架简介 --junit、jmock、mockito、powermock的简单使用
转 单元测试及框架简介 --junit.jmock.mockito.powermock的简单使用 2013年08月28日 14:33:06 luvinahlc 阅读数:6413 标签: 测试工具单元测 ...
- 使用MRUnit,Mockito和PowerMock进行Hadoop MapReduce作业的单元测试
0.preliminary 环境搭建 Setup development environment Download the latest version of MRUnit jar from Apac ...
- springboot集成mockito与powermock
mockito大家都比较熟悉了,存在或者不存在,都不要紧,mockito让你有一种只要一出手,就知道有没有的感觉.但是它也不是万能的,比如静态方法.私有方法,它就无能为力了.这是为什么呢?当然不是 ...
- Sphinx(coreseek) 安装使用以及词库的扩展
1.Sphinx(coreseek) 是啥 一般而言,Sphinx是一个独立的全文搜索引擎:而Coreseek是一个支持中文的全文搜索引擎,意图为其他应用提供高速.低空间占用.高结果相关度的中文全文搜 ...
- PHP基础库及扩展库安装
一.安装PHP所需的lib库(基础库): 1.yum install zlib-devel libxml2-devel libjpey-devel libjpeg-turbo-devel libico ...
- 使用远程接口库进一步扩展Robot Framework的测试能力
引言: Robot Framework的四层结构已经极大的提高了它的扩展性.我们可以使用它丰富的扩展库来完成大部分测试工作.可是碰到下面两种情况,仅靠四层结构就不好使了: 1.有些复杂的测试可能跨越多 ...
- Python模块进阶、标准库、扩展库
模块进阶 Python有一套很有用的标准库(standard library).标准库会随着Python解释器,一起安装在你的电脑中的. 它是Python的一个组成部分.这些标准库是Python为你准 ...
- FFmpeg编码扩展之————编码库的扩展(libfdk-aac)
ffmpeg windows版没有libfdk-acc 请求该地址下载:http://tmod.nmm-hd.org/FFmpeg/
- GDAL库扩展Landsat系列MTL文件格式支持
Landsat系列卫星提供的数据,一般都是每个波段一个tif文件,然后外加一个MTL.txt的元数据文件,使用gdal可以直接打开每个波段的tif文件,但是有时候想在打开tif数据的同时能够自动读取M ...
随机推荐
- linux实用命令-待补充
- du 查看目录大小 - du -h 带有单位显示目录信息 - df 查看磁盘大小 - df -h 带有单位显示磁盘信息 - netstat 显示网络状态信息 - 清除僵尸进程 ps -eal | ...
- idea热部署设置(复制)
提出问题 IntelliJ IDEA工具如何设置热部署??? 解决问题 我的IDEA的版本是:IntelliJ IDEA 14.0.2 第一步:打开tomcat配置 这里写图片描述 第二步: 这里写图 ...
- 一些echarts的基本图形
先拿一个图形渲染过程举例 引用处 <bar ref="ARPUChart" v-if="ARPUChart" style="width:500p ...
- 在iBatis中操作Blob数据类型
这里的Blob数据类型指的是保存了文本的blob数据类型 直接读取blob类型存储的文本,可能会出现乱码,所以需要读取完后进行手动转码 这里使用ibatis作为持久层 SELECT urlconten ...
- (转)iOS-蓝牙学习资源博文收集
ios蓝牙开发(一)蓝牙相关基础知识 ios蓝牙开发(二)蓝牙中心模式的ios代码实现 ios蓝牙开发(三)app作为外设被连接的实现 ios蓝牙开发(四)BabyBluetooth蓝牙库介绍 暂未完 ...
- 【Luogu】P3320寻宝游戏(Splay)
题目链接 其实这题用Set就完事了但我不会Set 智商-=inf 求虚树上所有边权和的两倍. 具体方式就是splay把所有在虚树上的点存一下,(按照DFS序排序的)每次插入/删除会更新前驱和它.后继和 ...
- spring+xml集成测试(准备数据和验证项的外部文件化)
Spring的集成测试 单位测试和集成测试,我想大家都做过,一般情况下,一般逻辑且不需要操作数据库的情况比较适合于单位测试了.而对于一个数据库应用来说,集成测试可能比单元测试更重要,你可以想象,一个互 ...
- 三叉神经树 ( neuron )
三叉神经树 ( neuron ) 题目描述 计算神经学作为新兴的交叉学科近些年来一直是学术界的热点.一种叫做SHOI 的神经组织因为其和近日发现的化合物SHTSC 的密切联系引起了人们的极大关注. S ...
- docker集群之swarm
1.swarm是什么? Swarm是Docker公司自研发的容器集群管理系统,Swarm在早期是作为一个独立服务存在,在Docker Engine v1.12中集成了Swarm的集群管理和编排功能.可 ...
- 【10】【转】node 之 pipe机制----未理解
转载地址:http://blog.csdn.net/vieri_32/article/details/48376547 前言 前几天别人请教我关于pipe的问题,我发现我虽然用了nodejs很久,但是 ...