junit使用mock objects进行单元测试
上一篇我介绍了使用stub进行单元测试。那么mock objects和stub有何区别?什么情况下使用mock objects呢?
下面摘自junit in action书中的解释:
mock objects (或者简称为 mocks),非常适用于将某一部分代码与其他代问隔离开来,并对这部分代码进行测试。
mocks 替换了测试中与你的方法协作的对象,从而提供个隔离层。从这一点来讲,它与 stub 有些类似。不过相似之处也仅限于
此,因为mocks 并不实现任何逻辑 它们只是提供了一种使测试能够控制仿造类的所有业务逻辑方法行为的方法的空壳。
下面以一个账户转账的测试用例加以说明。大家可以比较下其与stub的区别。
创建账户类
package com.lhy.junitkaifa.mocks.account;
/**
* 账户
*
* @author xusucheng
* @create 2018-12-22
**/
public class Account {
private String accountId;
private long balance;
public Account(String accountId, long balance){
this.accountId = accountId;
this.balance = balance;
}
public void debit(long amount){
this.balance-=amount;
}
public void credit(long amount){
this.balance+=amount;
}
public long getBalance(){
return this.balance;
}
}
创建配置接口与实现
package com.lhy.junitkaifa.mocks.configurations;
/**
* 配置
*
* @author xusucheng
* @create 2018-12-22
**/
public interface Configuration {
String getSQL(String sqlString);
}
package com.lhy.junitkaifa.mocks.configurations;
/**
* @author xusucheng
* @create 2018-12-22
**/
public class DefaultConfiguration implements Configuration {
public DefaultConfiguration(String configuration){
}
@Override
public String getSQL(String sqlString) {
return null;
}
}
创建账户管理接口与实现
package com.lhy.junitkaifa.mocks.account;
/**
* 账户管理接口
*
* @author xusucheng
* @create 2018-12-22
**/
public interface AccountManager {
Account findAccountById(String userId);
void updateAccount(Account account);
}
package com.lhy.junitkaifa.mocks.account;
import com.lhy.junitkaifa.mocks.configurations.Configuration;
import com.lhy.junitkaifa.mocks.configurations.DefaultConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 账户管理接口实现
*
* @author xusucheng
* @create 2018-12-22
**/
public class AccountManagerImpl implements AccountManager {
private Log logger;
private Configuration configuration;
public AccountManagerImpl(){
this(LogFactory.getLog(AccountManagerImpl.class),new DefaultConfiguration("technical"));
}
public AccountManagerImpl(Log logger, Configuration configuration){
this.logger = logger;
this.configuration = configuration;
}
@Override
public Account findAccountById(String userId) {
this.logger.debug("Getting account for user["+userId+"]");
this.configuration.getSQL("FIND_ACCOUNT_BY_ID");
return null;
}
@Override
public void updateAccount(Account account) {
//Do nothing.
}
}
创建账户管理服务类
package com.lhy.junitkaifa.mocks.account;
/**
* 账户管理服务
*
* @author xusucheng
* @create 2018-12-22
**/
public class AccountService {
private AccountManager accountManager;
/**
* 注入具体实现
* @param accountManager
*/
public void setAccountManager(AccountManager accountManager){
this.accountManager = accountManager;
}
public void transfer(String fromId, String toId, long amount){
//转出账户
Account from = this.accountManager.findAccountById(fromId);
//转入账户
Account to = this.accountManager.findAccountById(toId);
from.debit(amount);
to.credit(amount);
this.accountManager.updateAccount(from);
this.accountManager.updateAccount(to);
}
}
创建测试用账户管理接口
package com.lhy.junitkaifa.mocks.account;
import java.util.HashMap;
import java.util.Map;
/**
* @author xusucheng
* @create 2018-12-22
**/
public class MockAccountManager implements AccountManager{
//保存所有账户
private Map<String,Account> accounts = new HashMap<>();
/**
* 添加账户
* @param userId
* @param account
*/
public void addAccount(String userId, Account account){
this.accounts.put(userId,account);
}
/**
* 获取账户
* @param userId
* @return
*/
@Override
public Account findAccountById(String userId) {
return this.accounts.get(userId);
}
@Override
public void updateAccount(Account account) {
//不干事
}
}
创建测试用日志类
package com.lhy.junitkaifa.mocks.account;
import org.apache.commons.logging.Log;
/**
* @author xusucheng
* @create 2018-12-22
**/
public class MockLog implements Log {
@Override
public boolean isFatalEnabled() {
return false;
}
@Override
public boolean isErrorEnabled() {
return false;
}
@Override
public boolean isWarnEnabled() {
return false;
}
@Override
public boolean isInfoEnabled() {
return false;
}
@Override
public boolean isDebugEnabled() {
return false;
}
@Override
public boolean isTraceEnabled() {
return false;
}
@Override
public void fatal(Object o) {
}
@Override
public void fatal(Object o, Throwable throwable) {
}
@Override
public void error(Object o) {
}
@Override
public void error(Object o, Throwable throwable) {
}
@Override
public void warn(Object o) {
}
@Override
public void warn(Object o, Throwable throwable) {
}
@Override
public void info(Object o) {
}
@Override
public void info(Object o, Throwable throwable) {
}
@Override
public void debug(Object o) {
}
@Override
public void debug(Object o, Throwable throwable) {
}
@Override
public void trace(Object o) {
}
@Override
public void trace(Object o, Throwable throwable) {
}
}
创建测试用配置类
package com.lhy.junitkaifa.mocks.configurations;
/**
* @author xusucheng
* @create 2018-12-22
**/
public class MockConfiguration implements Configuration {
public void setSQL(String sqlString){
}
@Override
public String getSQL(String sqlString) {
return null;
}
}
创建测试类
package com.lhy.junitkaifa.mocks.account;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* @author xusucheng
* @create 2018-12-22
**/
public class TestAccountService {
@Test
public void testTransferOk(){
MockAccountManager mockAccountManager = new MockAccountManager();
Account from = new Account("1",200);
Account to = new Account("2",100);
mockAccountManager.addAccount("1", from);
mockAccountManager.addAccount("2", to);
AccountService accountService = new AccountService();
accountService.setAccountManager(mockAccountManager);
accountService.transfer("1","2",50);
assertEquals(150,from.getBalance());
assertEquals(150,to.getBalance());
}
}
运行测试
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构