JUnit单元测试:Mockito.doNothing()模拟void方法未达预期
兄弟我懂你这种头疼的感觉——明明用了doNothing()想跳过那个会抛异常的functionC,结果测试还是崩在异常上,简直怀疑人生对吧?我来帮你捋捋最可能的几个原因,以及怎么解决。
首先,咱们先对齐一下你的场景:你要测试ClassA的functionA,这个方法会调用ClassB的void方法functionB,而functionB内部又会调用functionC(就是这个家伙会抛异常)。你本来想用doNothing()模拟functionB,让它啥也不干,这样就不会走到functionC了,但测试还是因为functionC的异常挂了。
最常见的坑:被测试类里用的不是Mock的ClassB实例
这绝对是80%的人会犯的错!比如你的ClassA是这么写的:
public class ClassA { public void functionA() { ClassB b = new ClassB(); // 这里直接new了真实的ClassB! b.functionB(); } }
不管你在测试里怎么mockClassB,ClassA里用的都是自己new出来的真实对象,那doNothing()自然完全没用,肯定会走到真实的functionB和functionC逻辑里。
解决办法:给ClassA注入ClassB的实例
你得让ClassA依赖的ClassB是外部传入的,比如用构造函数注入:
public class ClassA { private final ClassB classB; // 通过构造函数把ClassB传进来 public ClassA(ClassB classB) { this.classB = classB; } public void functionA() { classB.functionB(); } }
这样在测试的时候,你就能把mock的ClassB传给ClassA,mock才会生效。
第二个坑:Mock的方法参数匹配不对
如果functionB是带参数的方法,比如void functionB(String id),而你mock的时候参数匹配错了,比如:
// 假设实际调用的是functionB("test123"),但你mock的是anyInt() doNothing().when(mockClassB).functionB(anyInt());
这时候Mockito会认为你mock的是另一个方法(参数类型不对),所以还是会走真实的functionB方法,进而触发functionC的异常。
解决办法:确保参数匹配准确
要么用匹配器精准匹配:
// 匹配任意字符串 doNothing().when(mockClassB).functionB(anyString()); // 或者匹配具体值 doNothing().when(mockClassB).functionB(eq("test123"));
要么就完全和实际调用的参数一致。
第三个坑:误用了Mockito的语法
对于void方法,你不能用普通的when()语法,比如:
// 错误写法!调用mockClassB.functionB()会执行真实方法! when(mockClassB.functionB()).doNothing();
因为when()里需要调用方法来捕获,但void方法调用后没有返回值,Mockito会直接执行真实的方法,那functionC自然会跑起来抛异常。
正确写法必须是前置doNothing():
doNothing().when(mockClassB).functionB();
这个语法才是告诉Mockito:当调用mock对象的functionB方法时,什么都不要做。
给你一个完整的正确示例
咱们把所有正确的代码拼起来,你对照着改:
被测试类ClassA(依赖注入版)
public class ClassA { private final ClassB classB; public ClassA(ClassB classB) { this.classB = classB; } public void functionA() { classB.functionB(); } }
ClassB的代码(包含会抛异常的functionC)
public class ClassB { public void functionB() { functionC(); } private void functionC() { throw new RuntimeException("这个异常会搞砸测试"); } }
测试类(正确写法)
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) public class ClassATest { @Mock private ClassB mockClassB; @Test public void testFunctionA() { // 把mock的ClassB传给ClassA ClassA classA = new ClassA(mockClassB); // 模拟void方法functionB,啥也不做 doNothing().when(mockClassB).functionB(); // 调用被测试方法 classA.functionA(); // 验证functionB确实被调用了 verify(mockClassB).functionB(); // 这时候functionC根本不会执行,测试顺利通过! } }
最后总结一下关键要点
- 一定要保证被测试类使用的是你mock出来的实例,而不是自己new的真实对象;
- 针对
void方法,必须用doNothing().when(mock).voidMethod()的语法; - 检查方法参数的匹配是否正确,别让参数不匹配导致mock失效。
按照这个思路改,你的测试肯定能正常跑起来!
内容的提问来源于stack exchange,提问作者Vamsi

