在开发中使用Mockito进行测试
关于单元测试的一些问题
当我们Javaweb项目中编写单元测试的时候,通常会面临一个普遍的问题:需要测试的类会有很多依赖,而这些依赖的类或者对象又会有很多别的依赖,导致我们在写单元测试的时候几乎需要把完整的业务体系代码编写出来,而在单元测试中将这这些个依赖完整的构建出来是一件很困难的事情,通常这个时候,我们会想到把里面一些需要的依赖“Mock”出来。
Mock及使用Mockito
Mock,从单词层面来讲师仿制和模拟,在软件开发和测试中通常是指模拟对象。
简单地说就是对测试的类所依赖的其他类和对象,进行mock - 构建它们的一个假的对象,定义这些假对象上的行为,然后提供给被测试对象使用。被测试对象像使用真的对象一样使用它们。用这种方式,我们可以把测试的目标限定于被测试对象本身,就如同在被测试对象周围做了一个划断,形成了一个尽量小的被测试目标。
通过这样,我们可以在不编写大量依赖对象的前提下使用少量的代码即可完成整个业务流程的单元测试。具体的使用可以参考下面一个业务中的简单例子。
Mock的框架有很多,最为知名的一个是Mockito,这是一个开源项目,使用广泛。官网:http://site.mockito.org/。
使用实例
- 创建项目时引入单元测试包和mockito的包
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>2.0.2-beta</version>
<scope>test</scope>
</dependency>
- 新建一个账户对象类 Account
public class Account {
/*
* 账户ID
*/
private String accountId;
/**
* 收支
*/
private long balance;
/**
* 初始化账户ID和收支
* @param accountId
* @param initialBalance
*/
public Account(String accountId, long initialBalance){
this.accountId = accountId;
this.balance = initialBalance;
}
public void debit(long amount){
this.balance -= amount;
}
public void credit(long amount){
this.balance += amount;
}
public long getBalance(){
return this.balance;
}
}
- 创建一个账号管理接口,里面包含了一个查找账户对象的方法和一个更新账户信息的方法
public interface AccountManager {
/**
* 根据userId找到对应账号对象
* @param userId
* @return
*/
Account findAccountForUser(String userId);
/**
* 更新账户信息
* @param account
*/
void updateAccount(Account account);
}
- 创建账号管理接口的实现类MockAccountManager,并在其中新增一个账号管理的方法
public class MockAccountManager implements AccountManager {
private Hashtable accounts = new Hashtable();
/**
* 新增一个添加账户的方法
* @param userId
* @param account
*/
public void addAccount(String userId, Account account) {
this.accounts.put(userId, account);
}
public Account findAccountForUser(String userId) {
return (Account) this.accounts.get(userId);
}
public void updateAccount(Account account) {
// do nothing
}
}
- 创建一个服务类AccountService,通过该类来调用接口中的方法
public class AccountService {
private AccountManager accountManager;
// private MockAccountManager mockAccountManager;
public void setAccountManager(MockAccountManager manager) {
accountManager = manager;
}
/**
*
* @param senderId 转账人的ID
* @param beneficiaryId 收款人ID
* @param amount 转账金额
*/
public void transfer(String senderId, String beneficiaryId, long amount) {
Account sender = accountManager.findAccountForUser(senderId);
Account beneficiary = accountManager.findAccountForUser(beneficiaryId);
//转账账户减少
sender.debit(amount);
//收款账户增加
beneficiary.credit(amount);
//更新转账账户信息
this.accountManager.updateAccount(sender);
//更新收款账户信息
this.accountManager.updateAccount(beneficiary);
}
}
- 创建一个普通的单元测试类来测试这个业务流程
import org.junit.Test;
import junit.framework.TestCase;
public class TestAccountService extends TestCase{
@Test
public void testTransferOk(){
//使用MockAccountManager
MockAccountManager mockAccountManager = new MockAccountManager();
//初始化两个账户
Account senderAccount = new Account("1", 200);
Account beneficiaryAccount = new Account("2", 100);
mockAccountManager.addAccount("1", senderAccount);
mockAccountManager.addAccount("2", beneficiaryAccount);
//初始化AccountService,将MockManager对象传入
AccountService accountService = new AccountService();
accountService.setAccountManager(mockAccountManager);
//转帐操作
accountService.transfer("1", "2", 50);
//验证
assertEquals(150, senderAccount.getBalance());
assertEquals(150, beneficiaryAccount.getBalance());
}
}
- 创建一个用Mockito进行测试的类
public class TestMockAccountService extends TestCase {
@Test
public void testTransferOk() {
Account senderAccount = new Account("1", 200);
Account beneficiaryAccount = new Account("2", 100);
// 使用mockito创建一个模拟对象
MockAccountManager mockAccountManager = Mockito.mock(MockAccountManager.class);
//mockito自带的控制流写法
when(mockAccountManager.findAccountForUser("1")).thenReturn(senderAccount);
when(mockAccountManager.findAccountForUser("2")).thenReturn(beneficiaryAccount);
AccountService accountService = new AccountService();
accountService.setAccountManager(mockAccountManager);
// 转帐操作
accountService.transfer("1", "2", 50);
// 验证
assertEquals(150, senderAccount.getBalance());
assertEquals(150, beneficiaryAccount.getBalance());
}
}
以上。
ps.突然发现直接直接从我有道云笔记拷贝不用修改代码格式,页面效果看起来也还挺棒...
在开发中使用Mockito进行测试的更多相关文章
- 测试开发中Django和Flask框架
Python测试开发中Django和Flask框架 为了更好地阐述这个问题,我们把开发一个应用的过程进行类比,往往开发一个应用(web应用.系统应用)跟建造房子的过程一样,需要先打地基,搭好骨架,然后 ...
- 【VS开发】在VS2010中开发ActiveX控件设置测试容器的方式
在VS2010中开发ActiveX控件设置测试容器的方式 借鉴文章http://blog.csdn.net/waxgourd0/article/details/7374669 在VS2010中开发MF ...
- Spring Boot项目中使用Mockito
本文首发于个人网站:Spring Boot项目中使用Mockito Spring Boot可以和大部分流行的测试框架协同工作:通过Spring JUnit创建单元测试:生成测试数据初始化数据库用于测试 ...
- TDD在Unity3D游戏项目开发中的实践
0x00 前言 关于TDD测试驱动开发的文章已经有很多了,但是在游戏开发尤其是使用Unity3D开发游戏时,却听不到特别多关于TDD的声音.那么本文就来简单聊一聊TDD如何在U3D项目中使用以及如何使 ...
- Java开发中的23种设计模式详解
[放弃了原文访问者模式的Demo,自己写了一个新使用场景的Demo,加上了自己的理解] [源码地址:https://github.com/leon66666/DesignPattern] 一.设计模式 ...
- 【初码干货】使用阿里云对Web开发中的资源文件进行CDN加速的深入研究和实践
提示:阅读本文需提前了解的相关知识 1.阿里云(https://www.aliyun.com) 2.阿里云CDN(https://www.aliyun.com/product/cdn) 3.阿里云OS ...
- C#开发中使用配置文件对象简化配置的本地保存
C#开发中使用配置文件对象简化配置的本地保存 0x00 起因 程序的核心是数据和逻辑,开发过程中免不了要对操作的数据进行设置,而有些数据在程序执行过程中被用户或程序做出的修改是应该保存下来的,这样程序 ...
- iOS开发中静态库之".framework静态库"的制作及使用篇
iOS开发中静态库之".framework静态库"的制作及使用篇 .framework静态库支持OC和swift .a静态库如何制作可参照上一篇: iOS开发中静态库之" ...
- iOS开发中静态库制作 之.a静态库制作及使用篇
iOS开发中静态库之".a静态库"的制作及使用篇 一.库的简介 1.什么是库? 库是程序代码的集合,是共享程序代码的一种方式 2.库的类型? 根据源代码的公开情况,库可以分为2种类 ...
随机推荐
- JavaScript实现AOP(面向切面编程)
什么是AOP? AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计.安全控制.异常处理等.把这些功能抽离出来之后, 再通过" ...
- Git 二分调试法,火速定位疑难Bug!
你一定遇到过,一个很久没修改过的功能,莫名其妙的出现了问题?肉眼查代码.屡逻辑完全找不到问题点?前两天还好好的功能,怎么这个今天就不行了?这两天改动了这么多代码,到底是那一次改动引发的 Bug? 这样 ...
- Ambari源代码分析之Resource.Type与ResourceProvider相应关系
前面提到.每一种Resource都相应一个ResourceProvider,以下给出其相应关系: Resource.Type ResourceProvider Workflow WorkflowRes ...
- TCP/IP具体解释--UDP数据报中的IP分片
1.UDP首部 2.UDP分片 在第二章,讲链路层是,提到过以太网.刨除数据帧帧头.最多传输的长度为1500.也就是说,假设一个ip数据报,长度大于1500,则须要分片. 分片方法: 在ip头中3位标 ...
- orale 查询每年、每月、每日统计量的sql语句
每年 select to_char(createtime, 'YYYY') 年, count(*) from table group by to_char(createtime, 'YYYY'); ...
- Linux禁用显示“缓冲调整”
Linux禁用显示"缓冲调整" youhaidong@youhaidong-ThinkPad-Edge-E545:~$ free -o total used free shared ...
- mysql千万级数据表,创建表及字段扩展的几条建议
一:概述 当我们设计一个系统时,需要考虑到系统的运行一段时间后,表里数据量大约有多少,如果在初期,就能估算到某几张表数据量非常庞大时(比如聊天消息表),就要把表创建好,这篇文章从创建表,增加数据,以及 ...
- mybatis的#{}占位符和${}拼接符的区别
#{}占位符:占位 如果传入的是基本类型,那么#{}中的变量名称可以随意写 如果传入的参数是pojo类型,那么#{}中的变量名称必须是pojo中的属性.属性.属性- ${}拼接符:字符串原样拼接 如果 ...
- 将java项目打包为jar
打开Eclipse,点击file,选择export 选择java,选择其中的JAR file并点击next 选择需要的到处的项目,并在下方输入将项目保存为的目录,文件名字. 如果,已经将项目打包为一个 ...
- win10 系统输入法与 idea的 ctr+shift+f 快捷键冲突,解决办法
我认为首先是输入法简繁热键的冲突,(当然也有人认为是qq的热键冲突,) 解决办法: 1.首先打开搜狗输入法的设置(当然有的可能不是搜狗输入法,其他的输入法设置步骤都是大同小异) 看到了吗,就是这个热键 ...