在开发中使用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.突然发现直接直接从我有道云笔记拷贝不用修改代码格式,页面效果看起来也还挺棒...

时间: 2024-10-27 05:53:44

在开发中使用Mockito进行测试的相关文章

对于软件开发中开发人员与测试人员关系的理解

在软件开发中都会有开发人员(以下简称开发)和测试人员(以下简称测试),在一些小型公司可能并没有测试,仅仅是开发兼任测试.在这里我仅针对于有专业的测试和专业的开发的项目. 每个公司应该都有考核机制,对于开发和测试的考核实际上很难量化,通常来讲大的方向就是开发所负责模块的bug数,对于测试来讲就是测出来的bug数,但这真的有效吗?这也许对开发有约束力,理论上开发是能够自己控制bug数的,如果从产生的bug数来评判开发的绩效还算有效,这样开发自然就会把代码写得更加认真.但如果根据测试测出来的bug数来

Spring Boot项目中使用Mockito

本文首发于个人网站:Spring Boot项目中使用Mockito Spring Boot可以和大部分流行的测试框架协同工作:通过Spring JUnit创建单元测试:生成测试数据初始化数据库用于测试:Spring Boot可以跟BDD(Behavier Driven Development)工具.Cucumber和Spock协同工作,对应用程序进行测试. 进行软件开发的时候,我们会写很多代码,不过,再过六个月(甚至一年以上)你知道自己的代码怎么运作么?通过测试(单元测试.集成测试.接口测试)可

Java开发中的23种设计模式详解(转)

设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周

python开发中常用的框架

以下是15个最受欢迎的Python开源框架.这些框架包括事件I/O,OLAP,Web开发,高性能网络通信,测试,爬虫等. Django: Python Web应用开发框架 Django 应该是最出名的Python框架,GAE甚至Erlang都有框架受它影响.Django是走大而全的方向,它最出名的是其全自动化的管理后台:只需要使用起ORM,做简单的对象定义,它就能自动生成数据库结构.以及全功能的管理后台. Diesel:基于Greenlet的事件I/O框架 Diesel提供一个整洁的API来编写

对软件开发中uml建模的理解和图形整理(一)

由于uml(统一建模语言)在开发中经常会用到,特别是在软件开发中的OOAD阶段,因此要理解和使用uml显得尤为重要.在uml开始之前,咱先回顾一个OOAD.OOP的主要特征. OOAD:根据面向对象的方法学来对软件系统进行分析和设计的过程.它包括OOA 分析阶段和OOD设计阶段.其中分析阶段主要解决"What to do?"的问题,而设计阶段主要解决"How to do?"的问题.具体来说就是:在OOA分析阶段咱要做的主要工作就是建立对业务问题域的视图(建立模型).

一探前端开发中的JS调试技巧

前言:调试技巧,在任何一项技术研发中都可谓是必不可少的技能.掌握各种调试技巧,必定能在工作中起到事半功倍的效果.譬如,快速定位问题.降低故障概率.帮助分析逻辑错误等等.而在互联网前端开发越来越重要的今天,如何在前端开发中降低开发成本,提升工作效率,掌握前端开发调试技巧尤为重要. 本文将一一讲解各种前端JS调试技巧,也许你已经熟练掌握,那让我们一起来温习,也许有你没见过的方法,不妨一起来学习,也许你尚不知如何调试,赶紧趁此机会填补空白. 骨灰级调试大师Alert 那还是互联网刚刚起步的时代,网页前

android权限--android开发中的权限及含义(上)

android权限--android开发中的权限及含义(上) android.permission.EXPAND_STATUS_BAR 允许一个程序扩展收缩在状态栏,android开发网提示应该是一个类似Windows Mobile中的托盘程序 android.permission.FACTORY_TEST 作为一个工厂测试程序,运行在root用户 android.permission.FLASHLIGHT 访问闪光灯,android开发网提示HTC Dream不包含闪光灯 android.pe

0. Java开发中的23种设计模式详解(转)

设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周

iOS开发中KVC、KVO简介

在iOS开发中,KVC和KVO是经常被用到的.可以使用KVC对对象的属性赋值和取得对象的属性值,可以使用KVO监听对象属性值的变化.简单介绍一下KVC和KVO. 一:键值编码(KVC) KVC,全称 Key Value Coding(键值编码),是OC 语言的一个特性,使用KVC,可以对对象的属性进行动态读写. KVC的操作方法由 NSKeyValueCoding协议提供,而NSObject已经实现了这个协议,因此OC中的几乎所有对象都可以使用KVC操作.常用的KVC操作方法有: (1)设置属性