Java新手疑问:JUnit测试应针对AccountService接口还是实现类?
关于JUnit+Mockito测试AccountService的困惑解答
嘿,作为Java新手刚接触JUnit和Mockito时,纠结测试接口还是实现类太正常啦!我来给你掰扯清楚:
你应该测试实现类AccountServiceImpl,而不是AccountService接口。原因很简单:
- 接口只是定义了方法的契约(要做什么),没有任何实际的业务逻辑代码,测试它根本没法验证功能是否正确;
- 所有的业务逻辑、数据操作、异常判断都在AccountServiceImpl里,你要验证的就是这些逻辑是否符合预期,同时也能间接确认实现类是否遵守了接口的契约。
接下来给你梳理下具体的测试思路,结合Mockito来处理依赖:
核心测试场景(以AccountServiceImpl为例)
因为你的实现类依赖了AccountRepository、OperationRepository和OperationConverter,我们可以用Mockito来模拟这些依赖,避免操作真实数据库,让测试更高效、独立。
1. 测试deposit方法
需要覆盖这些场景:
- 正常存款:验证余额更新正确,操作记录被保存,返回的Operation对象符合预期
- 存款金额为负数:抛出
OperationException - 目标账户不存在:抛出
AccountException
这里给你写个简单的测试示例:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class AccountServiceImplTest { @Mock private AccountRepository accountRepository; @Mock private OperationRepository operationRepository; @Mock private OperationConverter operationConverter; @InjectMocks private AccountServiceImpl accountService; // 测试实现类 @Test void deposit_ValidAmountAndExistingAccount_ReturnsOperation() { // 1. 准备测试数据 String accountNumber = "ACC123"; int depositAmount = 100; AccountEntity mockAccount = new AccountEntity(); mockAccount.setNumber(accountNumber); mockAccount.setBalance(200); OperationEntity savedOperation = new OperationEntity(); savedOperation.setBalance(300); Operation mockReturnedOperation = new Operation(); mockReturnedOperation.setBalance(300); // 2. 模拟依赖行为 when(accountRepository.findByNumber(accountNumber)).thenReturn(mockAccount); when(operationRepository.save(any(OperationEntity.class))).thenReturn(savedOperation); when(operationConverter.convert(savedOperation)).thenReturn(mockReturnedOperation); // 3. 执行测试方法 Operation result = accountService.deposit(accountNumber, depositAmount); // 4. 验证结果 assertEquals(300, result.getBalance()); verify(accountRepository, times(1)).save(mockAccount); // 确认账户余额被更新 verify(operationRepository, times(1)).save(any(OperationEntity.class)); // 确认操作记录被保存 } @Test void deposit_NegativeAmount_ThrowsOperationException() { // 验证金额为负时抛出异常 assertThrows(OperationException.class, () -> accountService.deposit("ACC123", -50)); } @Test void deposit_NonExistingAccount_ThrowsAccountException() { when(accountRepository.findByNumber("ACC999")).thenReturn(null); assertThrows(AccountException.class, () -> accountService.deposit("ACC999", 100)); } }
2. 测试withdraw方法
类似地,要覆盖:
- 正常取款成功,余额更新正确
- 取款金额超过当前余额:抛出
OperationException - 取款金额为负数:抛出
OperationException - 账户不存在:抛出
AccountException
3. 测试history方法
- 账户存在且有操作记录:返回包含对应操作的OperationsDto
- 账户存在但无操作记录:返回空的或空列表的OperationsDto
- 账户不存在:抛出
AccountException
额外小贴士
- 测试时要专注于业务逻辑的验证,而不是依赖的内部实现(比如不用关心Repository怎么保存数据,只需要确认它被调用了正确的次数、传入了正确的参数)
- 用Mockito的
verify方法来确认依赖方法是否被正确调用,用when来模拟依赖的返回值或异常
内容的提问来源于stack exchange,提问作者Amadou Beye
相关产品推荐
相关产品推荐

