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





目录
相关文章
|
21天前
|
Java API Maven
2025 Java 零基础到实战最新技术实操全攻略与学习指南
本教程涵盖Java从零基础到实战的全流程,基于2025年最新技术栈,包括JDK 21、IntelliJ IDEA 2025.1、Spring Boot 3.x、Maven 4及Docker容器化部署,帮助开发者快速掌握现代Java开发技能。
221 1
|
27天前
|
JavaScript 安全 前端开发
Java开发:最新技术驱动的病人挂号系统实操指南与全流程操作技巧汇总
本文介绍基于Spring Boot 3.x、Vue 3等最新技术构建现代化病人挂号系统,涵盖技术选型、核心功能实现与部署方案,助力开发者快速搭建高效、安全的医疗挂号平台。
118 3
|
28天前
|
存储 人工智能 算法
Java 大视界 -- Java 大数据在智能医疗影像数据压缩与传输优化中的技术应用(227)
本文探讨 Java 大数据在智能医疗影像压缩与传输中的关键技术应用,分析其如何解决医疗影像数据存储、传输与压缩三大难题,并结合实际案例展示技术落地效果。
|
28天前
|
机器学习/深度学习 算法 Java
Java 大视界 -- Java 大数据在智能物流运输车辆智能调度与路径优化中的技术实现(218)
本文深入探讨了Java大数据技术在智能物流运输中车辆调度与路径优化的应用。通过遗传算法实现车辆资源的智能调度,结合实时路况数据和强化学习算法进行动态路径优化,有效提升了物流效率与客户满意度。以京东物流和顺丰速运的实际案例为支撑,展示了Java大数据在解决行业痛点问题中的强大能力,为物流行业的智能化转型提供了切实可行的技术方案。
|
30天前
|
分布式计算 Cloud Native 安全
Java:历久弥新的技术基石
Java:历久弥新的技术基石
|
30天前
|
分布式计算 Java 大数据
Java:技术生态中的常青树
Java:技术生态中的常青树
|
1月前
|
人工智能 自然语言处理 分布式计算
AI 驱动传统 Java 应用集成的关键技术与实战应用指南
本文探讨了如何将AI技术与传统Java应用集成,助力企业实现数字化转型。内容涵盖DJL、Deeplearning4j等主流AI框架选择,技术融合方案,模型部署策略,以及智能客服、财务审核、设备诊断等实战应用案例,全面解析Java系统如何通过AI实现智能化升级与效率提升。
157 0
|
3月前
|
Java 测试技术 容器
Jmeter工具使用:HTTP接口性能测试实战
希望这篇文章能够帮助你初步理解如何使用JMeter进行HTTP接口性能测试,有兴趣的话,你可以研究更多关于JMeter的内容。记住,只有理解并掌握了这些工具,你才能充分利用它们发挥其应有的价值。+
683 23
|
8月前
|
数据可视化 前端开发 测试技术
接口测试新选择:Postman替代方案全解析
在软件开发中,接口测试工具至关重要。Postman长期占据主导地位,但随着国产工具的崛起,越来越多开发者转向更适合中国市场的替代方案——Apifox。它不仅支持中英文切换、完全免费不限人数,还具备强大的可视化操作、自动生成文档和API调试功能,极大简化了开发流程。
|
5月前
|
SQL 安全 测试技术
2025接口测试全攻略:高并发、安全防护与六大工具实战指南
本文探讨高并发稳定性验证、安全防护实战及六大工具(Postman、RunnerGo、Apipost、JMeter、SoapUI、Fiddler)选型指南,助力构建未来接口测试体系。接口测试旨在验证数据传输、参数合法性、错误处理能力及性能安全性,其重要性体现在早期发现问题、保障系统稳定和支撑持续集成。常用方法包括功能、性能、安全性及兼容性测试,典型场景涵盖前后端分离开发、第三方服务集成与数据一致性检查。选择合适的工具需综合考虑需求与团队协作等因素。
572 24