Java单元测试——Mock技术配置

简介: Java单元测试——Mock技术配置

1. 源代码


AccountService.java
package 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的是这个类。这几个类的类关系图如下:


image.png通常的调用方法如下:

@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.java
package 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

image.png

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() );
  }
}





目录
相关文章
|
6天前
|
运维 关系型数据库 MySQL
os-copilot安装_配置_功能测试全集
我是一位中级运维工程师,我平时工作会涉及到 各类服务器的 数据库 与 java环境配置 操作。 我顺利使用了OS Copilot的 -t -f | 功能,我的疑惑是不能在自动操作过程中直接给与脚本运行权限,必须需要自己运行一下 chmod 这个既然有了最高的权限,为什么就不能直接给与运行权限呢。 我认为 -t 功能有用,能解决后台运行基础命令操作。 我认为 -f 功能有用,可以通过task文件中撰写连续任务操作。 我认为 | 对文件理解上有很直接的解读,可以在理解新程序上有很大帮助。
156 84
|
5天前
|
人工智能 Ubuntu Linux
os-copilot使用之全面配置与使用测试
作为一名个人开发者,我主要从事云服务器架设工作。近期,我成功使用了OS Copilot的 `-t -f |` 功能,解决了执行语句、连续提问及快速理解文件的问题。我发现这些功能非常实用,特别是在使用Workbench时能快速调用AI助手。此外,建议将AI功能与xShell工具联动,进一步提升效率。文中详细记录了购买服务器、远程连接、安装配置OS Copilot以及具体命令测试的过程,展示了如何通过快捷键和命令行操作实现高效开发。
105 67
|
3月前
|
Java 测试技术 开发者
必学!Spring Boot 单元测试、Mock 与 TestContainer 的高效使用技巧
【10月更文挑战第18天】 在现代软件开发中,单元测试是保证代码质量的重要手段。Spring Boot提供了强大的测试支持,使得编写和运行测试变得更加简单和高效。本文将深入探讨Spring Boot的单元测试、Mock技术以及TestContainer的高效使用技巧,帮助开发者提升测试效率和代码质量。
469 2
|
8天前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
5天前
|
Java Linux iOS开发
如何配置 Java 环境变量:设置 JAVA_HOME 和 PATH
本文详细介绍如何在Windows和Linux/macOS系统上配置Java环境变量。
179 12
|
1月前
|
域名解析 弹性计算 监控
slb测试基本配置检查
slb测试基本配置检查
109 60
|
9天前
|
Java
CentOS7.8配置Adoptium-Java17运行环境
本指南介绍如何设置清华镜像源并安装 Temurin-17-JRE 运行环境。首先,编辑 `/etc/yum.repos.d/adoptium.repo` 文件,配置清华镜像源。接着,使用 `yum install -y temurin-17-jre` 命令安装 Temurin-17-JRE,并通过 `java --version` 验证安装成功。相关配置和操作界面截图附后。
25 8
|
5天前
|
前端开发 NoSQL Java
【Java若依框架】RuoYi-Vue的前端和后端配置步骤和启动步骤
本文介绍了如何配置和启动基于Java的若依(RuoYi)项目,涵盖后端和前端的详细步骤。首先,准备Redis、MySQL以及IDE(如Idea和VS)。接着,通过GitHub获取代码并导入到IDE中,执行必要的SQL文件和配置数据库密码。然后,启动Redis并进行相关配置。最后,按照前端配置步骤克隆前端代码库,打开终端执行命令完成前端配置。整个过程详细记录了每一步的操作,帮助开发者顺利部署若依项目。 如果你觉得有帮助,请点赞、关注和收藏,这将是我持续分享的动力!
83 1
|
5天前
|
前端开发 Java 开发工具
Git使用教程-将idea本地Java等文件配置到gitte上【保姆级教程】
本内容详细介绍了使用Git进行版本控制的全过程,涵盖从本地仓库创建到远程仓库配置,以及最终推送代码至远程仓库的步骤。
18 0
|
1月前
|
监控 负载均衡 容灾
slb测试配置
slb测试配置
43 5