单元测试利器——Mockito
2015-10-10 13:40
344 查看
Stub和Mock:
Stub(桩)对象用来提供测试时所需要的测试数据,可以对各种交互设置相应的回应。例如我们可以设置方法调用的返回值等等。Mockito中的when(...).thenReturn(...)这样的语法便是设置方法调用的返回值。也可以设置在方法何时调用会抛出异常等。
Mock对象用来验证测试中所依赖对象间的交互是否能够达到预期。Mockito中用verify(...).methodXxx(...)语法来验证methodXxx方法是否按照预期被调用。
在了解利器之前,先了解一下一般武器——Mockito
Mockito是一种mock工具/框架。我理解EasyMock有点过时了,Mockito是现在比较流行的。
什么是mock?说的直白一点,大家都知道unit test应该是尽可能独立的。对一个class的unit test不应该再和其他class有任何交互。
现在有一个类,扫描一个目录并将找到的文件都上传到FTP server。该类对于不同的FTP响应(找不到FTP server 或 上传成功,或上传失败),有一些后续操作。
在写这个类的UT时,我们就必须虚构出来一个FTP对象。这样在UT中,这个虚构的对象能够代替真正的FTP,对被测试类的调用做出一定的响应。从而知道被测试类是否正确的调用了FTP并做出一些正确的期望的响应。从而达到测试的目的。
mock可以模拟各种各样的对象,从而代替真正的对象做出希望的响应。
mockito的基本使用方法:
1.需要创建一个mock对象来代替真的对象。
2.指定mock对象被调用时的返回值是第二步,比如指定让mockFTP对象第一次被调用时返回"找不到FTP server"。这一步一般称为stubbing。一般是when(mockedList.get(0)).thenReturn("first")的样子。
3.验证被测试类是否正确工作是第三步,使用verify()。例如,验证当mockFTP对象返回"找不到FTP server"时,测试代码是否按要求重试。
然后测试就完成啦~~
项目引入Mockito框架jar:
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.5</version>
<scope>test</scope>
</dependency>
</dependencies>
引入Mockito包:
import static org.mockito.Mockito.*;
常见的集中场景:
模拟方法调用的返回值:
比如
// 模拟获取第一个元素时,返回字符串first。 给特定的方法调用返回固定值在官方说法中称为stub。
when(mockedList.get(0)).thenReturn("first");
// 此时打印输出first
System.out.println(mockedList.get(0));
模拟方法调用抛出异常:
// 模拟获取第二个元素时,抛出RuntimeException
when(mockedList.get(1)).thenThrow(new RuntimeException());
// 此时将会抛出RuntimeException
System.out.println(mockedList.get(1));
没有返回值类型的方法也可以模拟异常抛出:doThrow(new RuntimeException()).when(mockedList).clear();
模拟调用方法时的参数匹配:
// anyInt()匹配任何int参数,这意味着参数为任意值,其返回值均是element
when(mockedList.get(anyInt())).thenReturn("element");
// 此时打印是element
System.out.println(mockedList.get(999));
模拟方法调用次数:
// 调用add一次
mockedList.add("once");
// 下面两个写法验证效果一样,均验证add方法是否被调用了一次
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
还可以通过atLeast(int i)和atMost(int i)来替代time(int i)来验证被调用的次数最小值和最大值。
来源: </article/9408681.html>
默认情况下,对于所有有返回值且没有stub过的方法,mockito会返回相应的默认值。
对于内置类型会返回默认值,如int会返回0,布尔值返回false。对于其他type会返回null。
这里一个重要概念就是: mock对象会覆盖整个被mock的对象,因此没有stub的方法只能返回默认值。
参数匹配:
在上例中如果想实现get(任意整数)时,都返回“element”时,该怎么做呢?很简单。
//stubbing 使用了内置的anyint() matcher.
when(mockedList.get(anyInt())).thenReturn("element");
使用 Mockito在做参数匹配的时候,没有考虑到输入参数为空的情况,导致matcher匹配不上,进而stub的行为无法生效。
其实在发现mock对象没有想自己想象的方式工作时,最好的方法就是debug进去,首先要先确定mock对象是不是真的传递进去了。然后再一步步的debug。
通常遇到的两种情况就是1,mock对象没有传递进去。2,参数没有匹配上。
较为复杂的测试实例:
因为在实际使用中,最常遇到的就是需要模拟第三方类库的行为。
比如现在有一个类FTPFileTransfer,实现了向FTP传输文件的功能。这个类中使用了apache的ftp类org.apache.commons.net.ftp.FTPClient;
现在测试FTPFileTransfer 这个类中的isFTPConnected方法, 希望模拟无法连接ftp的情况,测试是否记录了错误log:
public class FTPFileTransfer{ //为了测试当ftp链接不上时,是否真的会记log,我们必须mock一个假的FTPClient对象,用该对象传递/覆盖掉真实的FTPClient对象ftp,并强制让这个假对象返回"无法连接",然后看是否会记log.
private FTPClient ftp;
private boolean isFTPConnected(){
if (!ftp.isConnected()) {
LOGGER.error("Disconnected from FTP.");
}
}
因此使用mock需要解决的问题是: 如何用mock的FTP对象覆盖掉真实代码中调用的FTPClient。因此需要将mock对象传递进去。
这里对源代码有一个限制:
源代码中必须使用set和get方法来设置/获得ftp对象,这样测试代码可以使用set来传递mock对象。
或者测试代码中写一个方法覆盖掉源代码中实例化ftp对象的方法,且测试代码中使用mock ftp对象。
使用set和get方法来设置/获得ftp对象
//使用mock()方法创建一个FTP的mock对象mockedFTP。
FTPClient mockedFTP = mock(FTPClient.class);
//Stub “无法连接”
when(mockedFTP.isConnected())).thenReturn("无法连接");
//写一个get方法返回FTPFileTransfer的实例用来测试,将mockedFTP作为参数传递进去。同时在这个方法内部用set方法将FTPFileTransfer类中的成员变量FTPClient ftp更改为mockFTP。
//这样我们就得到了一个FTPFileTransfer的实例,同时里面的ftp已经变成了我们希望的mockFTP。
private FTPFileTransfer getMockTaskFileTransfer(final FTPClientmockedFTP) {
FTPFileTransfer test = new FTPFileTransfer("127.0.0.1", 8888, "//usr", "username", "password");
test.setFTPClient(mockedFTP);
return test;
}
@Test
public void testTransfer() throws SocketException, IOException{
FTPFileTransfer test = getMockTaskFileTransfer();
//得到这个实例以后,就直接调用这个实例的isFTPConnected方法,然后去log文件里找有没有我们希望的log就行了。注意此时,mockedFTP一定会返回"无法连接",所以isFTPConnected一定会记log。
test.isFTPConnected();
}
第二种方式:
从名字就可以看出,通过创建被测试类的子类,覆盖掉被测试类的getFTPClient()方法,将mock对象传递进去。
class MockFTPFileTransfer extends FTPFileTransfer{
public MockFTPFileTransfer(){
super("127.0.0.1", 8888, "//usr", "username", "password");
}
//源代码中必须使用get来获得ftp对象,否则mock不会生效
@Override
public FTPClient getFTPClient(){
FTPClient mockedFTP = mock(FTPClient.class);
when(mockedFTP.isConnected()).thenReturn(true);
return mockedFTP;
}
}
@Test
public void testTransfer() throws SocketException, IOException{
FTPFileTransfer test = new MockFTPFileTransfer();
test.isFTPConnected();
}
大家也可以去参考Mockito的官网,这里仅是一个入门而已,算是抛砖引玉了。
Stub(桩)对象用来提供测试时所需要的测试数据,可以对各种交互设置相应的回应。例如我们可以设置方法调用的返回值等等。Mockito中的when(...).thenReturn(...)这样的语法便是设置方法调用的返回值。也可以设置在方法何时调用会抛出异常等。
Mock对象用来验证测试中所依赖对象间的交互是否能够达到预期。Mockito中用verify(...).methodXxx(...)语法来验证methodXxx方法是否按照预期被调用。
在了解利器之前,先了解一下一般武器——Mockito
Mockito是一种mock工具/框架。我理解EasyMock有点过时了,Mockito是现在比较流行的。
什么是mock?说的直白一点,大家都知道unit test应该是尽可能独立的。对一个class的unit test不应该再和其他class有任何交互。
现在有一个类,扫描一个目录并将找到的文件都上传到FTP server。该类对于不同的FTP响应(找不到FTP server 或 上传成功,或上传失败),有一些后续操作。
在写这个类的UT时,我们就必须虚构出来一个FTP对象。这样在UT中,这个虚构的对象能够代替真正的FTP,对被测试类的调用做出一定的响应。从而知道被测试类是否正确的调用了FTP并做出一些正确的期望的响应。从而达到测试的目的。
mock可以模拟各种各样的对象,从而代替真正的对象做出希望的响应。
mockito的基本使用方法:
1.需要创建一个mock对象来代替真的对象。
2.指定mock对象被调用时的返回值是第二步,比如指定让mockFTP对象第一次被调用时返回"找不到FTP server"。这一步一般称为stubbing。一般是when(mockedList.get(0)).thenReturn("first")的样子。
3.验证被测试类是否正确工作是第三步,使用verify()。例如,验证当mockFTP对象返回"找不到FTP server"时,测试代码是否按要求重试。
然后测试就完成啦~~
项目引入Mockito框架jar:
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.5</version>
<scope>test</scope>
</dependency>
</dependencies>
引入Mockito包:
import static org.mockito.Mockito.*;
常见的集中场景:
模拟方法调用的返回值:
比如
// 模拟获取第一个元素时,返回字符串first。 给特定的方法调用返回固定值在官方说法中称为stub。
when(mockedList.get(0)).thenReturn("first");
// 此时打印输出first
System.out.println(mockedList.get(0));
模拟方法调用抛出异常:
// 模拟获取第二个元素时,抛出RuntimeException
when(mockedList.get(1)).thenThrow(new RuntimeException());
// 此时将会抛出RuntimeException
System.out.println(mockedList.get(1));
没有返回值类型的方法也可以模拟异常抛出:doThrow(new RuntimeException()).when(mockedList).clear();
模拟调用方法时的参数匹配:
// anyInt()匹配任何int参数,这意味着参数为任意值,其返回值均是element
when(mockedList.get(anyInt())).thenReturn("element");
// 此时打印是element
System.out.println(mockedList.get(999));
模拟方法调用次数:
// 调用add一次
mockedList.add("once");
// 下面两个写法验证效果一样,均验证add方法是否被调用了一次
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
还可以通过atLeast(int i)和atMost(int i)来替代time(int i)来验证被调用的次数最小值和最大值。
来源: </article/9408681.html>
默认情况下,对于所有有返回值且没有stub过的方法,mockito会返回相应的默认值。
对于内置类型会返回默认值,如int会返回0,布尔值返回false。对于其他type会返回null。
这里一个重要概念就是: mock对象会覆盖整个被mock的对象,因此没有stub的方法只能返回默认值。
参数匹配:
在上例中如果想实现get(任意整数)时,都返回“element”时,该怎么做呢?很简单。
//stubbing 使用了内置的anyint() matcher.
when(mockedList.get(anyInt())).thenReturn("element");
使用 Mockito在做参数匹配的时候,没有考虑到输入参数为空的情况,导致matcher匹配不上,进而stub的行为无法生效。
其实在发现mock对象没有想自己想象的方式工作时,最好的方法就是debug进去,首先要先确定mock对象是不是真的传递进去了。然后再一步步的debug。
通常遇到的两种情况就是1,mock对象没有传递进去。2,参数没有匹配上。
较为复杂的测试实例:
因为在实际使用中,最常遇到的就是需要模拟第三方类库的行为。
比如现在有一个类FTPFileTransfer,实现了向FTP传输文件的功能。这个类中使用了apache的ftp类org.apache.commons.net.ftp.FTPClient;
现在测试FTPFileTransfer 这个类中的isFTPConnected方法, 希望模拟无法连接ftp的情况,测试是否记录了错误log:
public class FTPFileTransfer{ //为了测试当ftp链接不上时,是否真的会记log,我们必须mock一个假的FTPClient对象,用该对象传递/覆盖掉真实的FTPClient对象ftp,并强制让这个假对象返回"无法连接",然后看是否会记log.
private FTPClient ftp;
private boolean isFTPConnected(){
if (!ftp.isConnected()) {
LOGGER.error("Disconnected from FTP.");
}
}
因此使用mock需要解决的问题是: 如何用mock的FTP对象覆盖掉真实代码中调用的FTPClient。因此需要将mock对象传递进去。
这里对源代码有一个限制:
源代码中必须使用set和get方法来设置/获得ftp对象,这样测试代码可以使用set来传递mock对象。
或者测试代码中写一个方法覆盖掉源代码中实例化ftp对象的方法,且测试代码中使用mock ftp对象。
使用set和get方法来设置/获得ftp对象
//使用mock()方法创建一个FTP的mock对象mockedFTP。
FTPClient mockedFTP = mock(FTPClient.class);
//Stub “无法连接”
when(mockedFTP.isConnected())).thenReturn("无法连接");
//写一个get方法返回FTPFileTransfer的实例用来测试,将mockedFTP作为参数传递进去。同时在这个方法内部用set方法将FTPFileTransfer类中的成员变量FTPClient ftp更改为mockFTP。
//这样我们就得到了一个FTPFileTransfer的实例,同时里面的ftp已经变成了我们希望的mockFTP。
private FTPFileTransfer getMockTaskFileTransfer(final FTPClientmockedFTP) {
FTPFileTransfer test = new FTPFileTransfer("127.0.0.1", 8888, "//usr", "username", "password");
test.setFTPClient(mockedFTP);
return test;
}
@Test
public void testTransfer() throws SocketException, IOException{
FTPFileTransfer test = getMockTaskFileTransfer();
//得到这个实例以后,就直接调用这个实例的isFTPConnected方法,然后去log文件里找有没有我们希望的log就行了。注意此时,mockedFTP一定会返回"无法连接",所以isFTPConnected一定会记log。
test.isFTPConnected();
}
第二种方式:
从名字就可以看出,通过创建被测试类的子类,覆盖掉被测试类的getFTPClient()方法,将mock对象传递进去。
class MockFTPFileTransfer extends FTPFileTransfer{
public MockFTPFileTransfer(){
super("127.0.0.1", 8888, "//usr", "username", "password");
}
//源代码中必须使用get来获得ftp对象,否则mock不会生效
@Override
public FTPClient getFTPClient(){
FTPClient mockedFTP = mock(FTPClient.class);
when(mockedFTP.isConnected()).thenReturn(true);
return mockedFTP;
}
}
@Test
public void testTransfer() throws SocketException, IOException{
FTPFileTransfer test = new MockFTPFileTransfer();
test.isFTPConnected();
}
大家也可以去参考Mockito的官网,这里仅是一个入门而已,算是抛砖引玉了。
相关文章推荐
- 高效的加载图片2.通过子线程(AsyncTask)加载图片
- MFC多字符集
- 如何使用Apache的ab工具进行网站性能测试
- python初学网页扒取
- 驱动程序收到意外的登录前响应。请验证连接属性,并检查 SQL Server 的实例正在主机上运行,且在此端口接受 TCP/IP 连接。该驱动程序只能与 SQL Server 2000 或更高版本一起使
- 程序设计竞赛常用技巧精选
- maven mybatis 快速生成
- 多台服务器最好加上相同的machineKey
- Android SDK 墙内更新方法 速度杠杠的
- Jquery.validate表单验证
- java入门第一天 基本介绍 环境配置 小小测试(上)
- 自定义超简单SearchView搜索框
- MFC消除视图闪烁
- C#给文件夹添加权限
- MFC消除视图闪烁
- android中用Spannable在TextView中设置超链接、颜色、字体
- 【IOS 开发学习总结-OC-39】★★ios开发之 MVC 模式
- springmvc 中controller与jsp传值
- 导出IIS Log列表,导出站点下虚拟目录列表
- 信号接口-视频输出端口介绍