Java单元测试——Mock技术配置
1. 源代码
AccountService.javapackage com.account;
import com.account.Account;import com.account.AccountManager;
public class AccountService{ //使用的帐户管理器实现 private AccountManager accountManager;
//设置帐户管理器实现的设置方法 public void setAccountManager( AccountManager manager ){ this.accountManager = manager; }
//一个设置客户经理实现从账户到账户的senderId beneficiaryId setter方法。 //senderId:转出方Id //beneficiaryId:收益方Id //amount:金额 public void transfer( String senderId, String beneficiaryId, long amount ){ //初始化转出方与收益方,findAccountForUser为接口类方法 Account sender = this.accountManager.findAccountForUser( senderId ); Account beneficiary = this.accountManager.findAccountForUser( beneficiaryId );
//转入和收益 sender.debit( amount ); beneficiary.credit( amount ); //更新,updateAccount为接口类方法 this.accountManager.updateAccount( sender ); this.accountManager.updateAccount( beneficiary ); }}
Account.java
package com.account;
public class Account{ private String accountId; private long balance;
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; }}
AccountManager.java
package com.account;
import com.account.Account;public interface AccountManager{ Account findAccountForUser(String userId ); void updateAccount(Account account ); }
由于在这里AccountManager.java仅仅做了一个interface,我们主要Mock的是这个类。这几个类的类关系图如下:
通常的调用方法如下:
@Test public void testTransferOK() { Account sendAccount = new Account("1",200); Account beneficiaryAccount = new Account("2",100); AccountManager. updateAccount( senderAccount ); AccountManager.updateAccount( beneficiaryAccount ); AccountManager.findAccountForUser("1" ) AccountManager.findAccountForUser( "2" ) AccountService accountService = new AccountService(); accountService.setAccountManager(AccountManager); accountService.transfer("1","2",50); //转钱 Assertions.assertEquals(150,sendAccount.getBalance()); Assertions.assertEquals(150,beneficiaryAccount.getBalance()); }
2. 最通用的Mock技术
StubAccountManager.javapackage com.account;import java.util.HashMap;public class StubAccountManager implements AccountManager{ private HashMap<String,Account> accounts = new HashMap<String,Account>(); public void addAcount(String userId,Account account){ this.accounts.put(userId,account); } public Account findAccountForUser(String userId){ return this.accounts.get(userId); } public void updateAccount(Account account){ //do nothing }}
Account.java
import static org.junit.Assert.assertEquals;import org.junit.jupiter.api.Test;public class TestAccountService { @Test public void testTransferOK() { StubAccountManager stubAccountManager = new StubAccountManager(); //定义MockAccountManager类 Account sendAccount = new Account("1",200); //定义收钱方和出钱方两个Account Account beneficiaryAccount = new Account("2",100); stubAccountManager.addAcount("1", sendAccount); //初始化收钱方和出钱方HashMap stubAccountManager.addAcount("2", beneficiaryAccount); AccountService accountService = new AccountService(); //初始化AccountService类 accountService.setAccountManager(stubAccountManager); //初始化AccountManager accountService.transfer("1","2",50); //转钱 Assertions.assertEquals(150,sendAccount.getBalance()); //判断转换后收付方金额是否正确 Assertions.assertEquals(150,beneficiaryAccount.getBalance()); }}
3.EasyMock技术
EasyMock需要以下个jar包:easymock-2.4.jar和easymockclassextension-2.4.jar
TestAccountServiceEasyMock.java
package com.account;
import static org.easymock.EasyMock.createMock;import static org.easymock.EasyMock.replay;import static org.easymock.EasyMock.expect;import static org.easymock.EasyMock.verify;
import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.AfterEach;import org.junit.jupiter.api.Assertions;import org.junit.jupiter.api.DisplayName;import org.junit.jupiter.api.Test;
import com.account.Account;import com.account.AccountManager;import com.account.AccountService;
public class TestAccountServiceEasyMock { private AccountManager mockAccountManager;
@BeforeEach public void setUp(){ //初始化easyMock mockAccountManager = createMock("mockAccountManager", AccountManager.class ); }
@Test @DisplayName("测试转账") public void testTransferOk(){ Account senderAccount = new Account( "1", 200 ); Account beneficiaryAccount = new Account( "2", 100 ); //开始定义期望 mockAccountManager.updateAccount( senderAccount ); mockAccountManager.updateAccount( beneficiaryAccount );
//EasyMock的expect和replay方法 expect( mockAccountManager.findAccountForUser( "1" ) ).andReturn( senderAccount ); //期望返回senderAccount expect( mockAccountManager.findAccountForUser( "2" ) ).andReturn( beneficiaryAccount ); //期望返beneficiaryAccount replay( mockAccountManager );//切换到replay状态 Record-> replay,在replay状态才可以进行验证
AccountService accountService = new AccountService(); accountService.setAccountManager( mockAccountManager ); accountService.transfer( "1", "2", 50 );
Assertions.assertEquals( 150, senderAccount.getBalance() ); Assertions.assertEquals( 150, beneficiaryAccount.getBalance() ); }
@AfterEach public void tearDown(){ verify( mockAccountManager ); }}
4. JMock技术
JMock依赖下面11个jar包。另外JMock不完全兼容JUnit5
TestAccountServiceJMock.java
package com.account;
import org.jmock.integration.junit4.JMock;import org.jmock.integration.junit4.JUnit4Mockery;import org.jmock.Expectations;import org.jmock.Mockery;import org.junit.jupiter.api.Assertions;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.DisplayName;import org.junit.jupiter.api.Test;import org.junit.runner.RunWith;
import com.account.Account;import com.account.AccountManager;import com.account.AccountService;
@RunWith(JMock.class)public class TestAccountServiceJMock { /** * The mockery context that we use to create our mocks. */ private Mockery context = new JUnit4Mockery();
/** * The mock instance of the AccountManager to use. */ private AccountManager mockAccountManager; @BeforeEach public void setUp(){ mockAccountManager = context.mock( AccountManager.class ); } @Test @DisplayName("测试转账") public void testTransferOk() { final Account senderAccount = new Account( "1", 200 );
final Account beneficiaryAccount = new Account( "2", 100 );
context.checking( new Expectations() { { oneOf( mockAccountManager ).findAccountForUser( "1" ); will( returnValue( senderAccount ) ); oneOf( mockAccountManager ).findAccountForUser( "2" ); will( returnValue( beneficiaryAccount ) );
oneOf( mockAccountManager ).updateAccount( senderAccount ); oneOf( mockAccountManager ).updateAccount( beneficiaryAccount ); } } );
AccountService accountService = new AccountService(); accountService.setAccountManager( mockAccountManager ); accountService.transfer( "1", "2", 50 );
Assertions.assertEquals( 150, senderAccount.getBalance() ); Assertions.assertEquals( 150, beneficiaryAccount.getBalance() ); } }
4.1 One,one of
JMock2.4版以前:one;
JMock2.51版以后:one of。
- oneOf (anObject).doSomething(); will(returnValue(10));
- oneOf (anObject).doSomething(); will(returnValue(20));
- oneOf (anObject).doSomething(); will(returnValue(30));
第一次调用时会返回10,第二次会返回20,第三次会返回30.
4.2 atLeast(n).of
atLeast(1).of (anObject).doSomething();
will(onConsecutiveCalls( returnValue(10), returnValue(20), returnValue(30)));
这里atLeast (1)表明doSomething方法将至少被调用一次,但不超过3次。且调用的返回值分别是10、20、30.
语句 |
含义 |
---|---|
one (one of) |
调用应该是一次且仅一次。 |
exactly(times).of |
调用应该正好是n次。注:one(one of)是exactly(1) 速写。 |
atLeast(times).of |
至少需要调用n次 |
atMost(times).of |
调用最多应为n次。 |
between(min, max).of |
调用至少应为min次,最多为max次。 |
allowing |
允许调用任意次数,但不必发生。 |
ignoring |
和allowing一样。应选择允许或忽略,以使测试代码清楚地表达意图。 |
never |
根本不需要调用。这是用来使测试更加明确,从而更容易理解。 |
5. mockito技术
需要mockito-all-1.9.5.jar包。
package com.account;import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Assertions;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.DisplayName;import org.junit.jupiter.api.Test;import org.mockito.Mockito;
import com.account.Account;import com.account.AccountManager;import com.account.AccountService;
public class TestAccountServiceMockito { private AccountManager mockAccountManager; private Account senderAccount; private Account beneficiaryAccount; @BeforeEach public void setUp(){ mockAccountManager = Mockito.mock(AccountManager.class); senderAccount = new Account( "1", 200 ); beneficiaryAccount = new Account( "2", 100 ); mockAccountManager.updateAccount( senderAccount ); mockAccountManager.updateAccount( beneficiaryAccount ); when(mockAccountManager.findAccountForUser("1")).thenReturn( senderAccount ); when(mockAccountManager.findAccountForUser("2")).thenReturn( beneficiaryAccount ); } @Test @DisplayName("测试转账") public void test() { AccountService accountService = new AccountService(); accountService.setAccountManager( mockAccountManager ); accountService.transfer( "1", "2", 50 );
Assertions.assertEquals( 150, senderAccount.getBalance() ); Assertions.assertEquals( 150, beneficiaryAccount.getBalance() ); }
}
- RabbitMQ入门-消息订阅模式
- 年终盘点:2018最值得学习的几种热门编程语言
- 如何编写没有Try/Catch的程序
- RabbitMQ入门-消息派发那些事儿
- RabbitMQ入门-高效的Work模式
- 谈谈分布式事务之四: 两种事务处理协议OleTx与WS-AT
- RabbitMQ入门-从HelloWorld开始
- RabbitMQ入门-从HelloWorld开始
- RabbitMQ入门-初识RabbitMQ
- 谈谈分布式事务之三: System.Transactions事务详解[下篇]
- 当InternalsVisibleToAttribute特性遭遇"强签名"
- MyBatis-从查询昨天的数据说起
- WCF并发(Concurrency)的本质:同一个服务实例上下文(InstanceContext)同时处理多个服务调用请求
- Spring集成RabbitMQ-必须知道的几个概念
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Android7.0上某些PopuWindow出现显示位置不正确问题的解决方法
- 有效的括号
- Android PopupWindow实现左侧弹窗效果
- LeetCode. 209. 长度最小的子数组
- 一篇文章就能了解Rxjava
- 二叉树遍历非递归程序 -- 使用栈模拟系统栈
- Android开发之多媒体文件获取工具类实例【音频,视频,图片等】
- 二叉树及leetcode练习题
- 利用递归函数的返回值
- Android DataBinding的官方双向绑定示例
- 整数拆分
- Android Drawerlayout实现侧滑菜单效果
- 分割等和子集
- LeetCode--打家劫舍问题
- Android 中ListView和GridView赋值错位