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。
第一次调用时会返回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() ); }
}