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

时间:2022-07-24
本文章向大家介绍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() );  }

}