Mockito中验证私有重载Void方法下游调用的问题
解决Mockito测试私有重载方法调用的问题
核心思路
Mockito不支持直接验证私有方法的调用,也不建议把私有方法的调用作为测试目标——私有方法属于内部实现细节,我们应该聚焦于测试Foo类的外部可见行为:即调用public void foo(Customer)后,下游Bar类的方法是否按预期被调用,同时覆盖长度大于/小于5的分支逻辑。
你的测试失败核心原因是:Foo类中依赖的Bar实例不是你Mock的那个,导致验证Bar方法调用时,实际调用的是真实Bar对象,而非Mock实例。
步骤1:确保Bar实例可注入(关键)
首先需要让Foo类的Bar依赖可被替换,推荐用构造注入(如果无法修改代码,后续提供反射方案):
public class Foo { private final Bar bar; // 添加构造方法注入Bar,方便测试时传入Mock实例 public Foo(Bar bar) { this.bar = bar; } public void foo(Customer customer) { if (customer.length() > 5) { foo(customer.length(), customer.name()); } else { return; } } private void foo(int length, String name) { bar.bar(length, name); } }
步骤2:正确编写测试用例
使用JUnit 5 + Mockito编写测试,覆盖两个场景:
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.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class FooTest { @Mock private Bar mockBar; // 模拟Bar实例 @InjectMocks private Foo foo; // 自动将mockBar注入到Foo中 // 场景1:客户长度>5时,Bar的bar()被调用1次 @Test void whenCustomerLengthOver5_thenBarMethodIsCalled() { // 构造测试用Customer Customer longCustomer = mock(Customer.class); when(longCustomer.length()).thenReturn(6); when(longCustomer.name()).thenReturn("Alice"); // 执行被测方法 foo.foo(longCustomer); // 验证public方法调用次数 verify(foo, times(1)).foo(longCustomer); // 验证Bar的bar方法按预期参数调用1次 verify(mockBar, times(1)).bar(6, "Alice"); } // 场景2:客户长度<=5时,Bar的bar()不被调用 @Test void whenCustomerLengthUnder5_thenBarMethodIsNotCalled() { // 构造测试用Customer Customer shortCustomer = mock(Customer.class); when(shortCustomer.length()).thenReturn(3); // 执行被测方法 foo.foo(shortCustomer); // 验证public方法调用次数 verify(foo, times(1)).foo(shortCustomer); // 验证Bar的bar方法从未被调用 verify(mockBar, never()).bar(anyInt(), anyString()); } }
若无法修改Foo类代码的兼容方案
如果不能给Foo添加构造方法,可通过反射强制替换Foo中的私有bar字段:
@Test void testWithReflection() { Bar mockBar = mock(Bar.class); Foo foo = new Foo(); // 用反射设置Foo的bar字段为mock实例 try { Field barField = Foo.class.getDeclaredField("bar"); barField.setAccessible(true); barField.set(foo, mockBar); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } // 后续测试逻辑和上面一致 Customer longCustomer = mock(Customer.class); when(longCustomer.length()).thenReturn(6); foo.foo(longCustomer); verify(mockBar, times(1)).bar(6, anyString()); }
你之前操作的问题复盘
verify(bar)失败:因为Foo内部的bar是自己new的真实实例,和你Mock的bar不是同一个对象,所以验证次数为0。- 不要尝试验证私有方法:Mockito不支持,且违背测试原则——我们只需要验证私有方法触发的外部行为(
Bar的调用)即可,无需关心内部调用细节。 doCallRealMethod()的正确用法:如果Mock了Foo,调用该方法后必须确保Foo依赖的Bar是Mock实例,否则还是会调用真实Bar的方法。
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

