You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何调用Helper方法的JUnit测试未按预期抛出异常?

问题:JUnit测试未按预期抛出异常的原因分析

相关代码

Service类

public class AccountServiceImpl {
    @Autowired
    AccountHelper accountHelper;

    @Override
    public Account createAccount(Account account) throws CustomerNotFoundException {
        accountHelper.checkAccountTypeForCustomer(account);
        return accountRepository.save(account);
    }
}

Helper类

public void checkAccountTypeForCustomer(Account acc) throws CustomerNotFoundException {
    Boolean customerExists = customerRepository.existsById(acc.getCustomerId());
    if(!customerExists) {
        throw new CustomerNotFoundException("604", Message.CUSTOMER_NOT_FOUND);
    }   
}

测试类

@ExtendWith(MockitoExtension.class)
public class AccountServiceTest {

    @Mock
    private AccountRepository accountRepository;

    @Mock
    private CustomerRepository customerRepository;

    @Mock
    private AccountHelper accountHelper;

    @InjectMocks 
    private AccountService testService;

    @Test
    void testCreateAccount() throws CustomerNotFoundException {
        Account account = Account.builder()
                .accountType(AccountType.SAVINGS)
                .openingBalance(BigDecimal.valueOf(3000))
                .ifsc("IFSC1")
                .customerId(1)
                .build();
        testService.createAccount(account);
    }
}

问题原因

你当前测试未抛出异常的核心问题是:你把AccountHelper声明成了@Mock对象。Mockito的Mock对象默认不会执行原类的真实方法逻辑,调用Mock对象的方法时只会做“空操作”——既不会执行检查客户是否存在的逻辑,也不会抛出异常,所以测试能顺利通过。

而当你把checkAccountTypeForCustomer()的逻辑移到Service方法内部时,这段逻辑属于Service的真实代码(没有被Mock),此时执行测试时会真实检查客户是否存在,自然会抛出异常导致测试失败,这符合预期。

修正方案

方案1:测试AccountHelper的真实逻辑

如果要验证AccountHelper的真实检查逻辑是否生效,不要把它声明为@Mock,改用@Spy(保留真实方法逻辑,同时可对其依赖的Mock对象进行配置):

@ExtendWith(MockitoExtension.class)
public class AccountServiceTest {

    @Mock
    private AccountRepository accountRepository;

    @Mock
    private CustomerRepository customerRepository;

    @Spy // 改用@Spy,保留AccountHelper的真实逻辑
    private AccountHelper accountHelper;

    @InjectMocks 
    private AccountService testService;

    @Test
    void testCreateAccount_ThrowsCustomerNotFound() {
        Account account = Account.builder()
                .accountType(AccountType.SAVINGS)
                .openingBalance(BigDecimal.valueOf(3000))
                .ifsc("IFSC1")
                .customerId(1)
                .build();
        
        // 模拟客户不存在的场景
        when(customerRepository.existsById(1)).thenReturn(false);
        
        // 断言调用createAccount时会抛出异常
        assertThrows(CustomerNotFoundException.class, () -> testService.createAccount(account));
    }
}

方案2:验证Service对AccountHelper的调用逻辑

如果只是要验证Service层是否正确调用了AccountHelper的方法,同时模拟异常场景,可以保留@Mock的AccountHelper,手动指定其方法行为:

@ExtendWith(MockitoExtension.class)
public class AccountServiceTest {

    @Mock
    private AccountRepository accountRepository;

    @Mock
    private CustomerRepository customerRepository;

    @Mock
    private AccountHelper accountHelper;

    @InjectMocks 
    private AccountService testService;

    @Test
    void testCreateAccount_ThrowsCustomerNotFound() {
        Account account = Account.builder()
                .accountType(AccountType.SAVINGS)
                .openingBalance(BigDecimal.valueOf(3000))
                .ifsc("IFSC1")
                .customerId(1)
                .build();
        
        // 模拟调用checkAccountTypeForCustomer时抛出异常
        doThrow(new CustomerNotFoundException("604", Message.CUSTOMER_NOT_FOUND))
            .when(accountHelper).checkAccountTypeForCustomer(account);
        
        // 断言会抛出异常
        assertThrows(CustomerNotFoundException.class, () -> testService.createAccount(account));
        
        // 验证AccountHelper的方法确实被Service调用
        verify(accountHelper).checkAccountTypeForCustomer(account);
    }
}

理解误区总结

你混淆了Mockito中Mock对象的作用:Mock对象是用来模拟依赖组件的行为,默认不会执行原类的真实方法逻辑。你原本以为Mock的AccountHelper会执行检查客户的逻辑,但实际上它只是一个“空壳”,不会触发任何真实逻辑,自然不会抛出异常。

内容的提问来源于stack exchange,提问作者Pavithra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 13:50:41