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的是这个类。这几个类的类关系图如下:
通常的调用方法如下:
@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
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() ); } }