JUnit Mockito测试:如何真实调用methodOne并Mock methodTwo
解决Mockito Spy中真实调用methodOne但Mock methodTwo的问题
我完全懂你遇到的这个坑——用Spy对象时明明想让methodOne真实跑起来,同时mock掉它调用的methodTwo,结果反而methodOne直接跳过执行,直奔断言环节。这其实是Mockito Spy的一个常见误区,咱们一步步拆解解决:
先明确你的业务类场景
假设你的FooService大概是这样的:
public class FooService { public String methodOne() { // 这里是你要真实执行的业务逻辑 String subResult = methodTwo(); return "methodOne最终结果: " + subResult; } public String methodTwo() { // 这是你想mock的依赖方法,可能有外部调用或耗时逻辑 return "真实methodTwo的返回值"; } }
错误写法的问题根源
很多人会下意识这么写测试,但这就是导致methodOne不执行的原因:
@Test public void testMethodOneWrong() { FooService spyFoo = Mockito.spy(new FooService()); // 这里的问题:when(spyFoo.methodTwo())会先触发一次真实的methodTwo调用,甚至可能打断后续流程 when(spyFoo.methodTwo()).thenReturn("mock的methodTwo返回值"); String result = spyFoo.methodOne(); assertEquals("methodOne最终结果: mock的methodTwo返回值", result); }
为什么失效?因为用when(spy.xxx()).thenReturn(...)处理Spy对象时,Mockito会先执行一次真实的xxx()方法;更关键的是,如果methodOne里调用的是this.methodTwo(),原始对象的方法调用可能绕开Spy代理,导致mock逻辑不生效,甚至如果methodTwo抛出异常,直接中断methodOne的执行。
正确的测试实现方式
核心是用doReturn().when()来mock Spy的方法,既不会触发真实方法调用,又能确保Spy代理拦截到methodOne内部对methodTwo的调用:
import org.junit.Test; import org.mockito.Mockito; import static org.junit.Assert.assertEquals; public class FooServiceTest { @Test public void testMethodOneCorrect() { // 1. 创建基于真实实例的Spy对象 FooService spyFoo = Mockito.spy(new FooService()); // 2. 用doReturn安全mock methodTwo,不会触发真实调用 Mockito.doReturn("mock的methodTwo返回值") .when(spyFoo) .methodTwo(); // 3. 真实调用methodOne,此时内部的methodTwo会被mock拦截 String actualResult = spyFoo.methodOne(); // 4. 验证结果是否符合预期 String expectedResult = "methodOne最终结果: mock的methodTwo返回值"; assertEquals(expectedResult, actualResult); // 可选:验证methodTwo确实被methodOne调用了一次 Mockito.verify(spyFoo, Mockito.times(1)).methodTwo(); } }
关键逻辑说明
doReturn().when()是Mockito为Spy对象设计的安全mock方式,它不像when().thenReturn()那样先执行真实方法,避免了不必要的副作用。- 当你调用
spyFoo.methodOne()时,Spy代理会接管这个调用,执行真实的methodOne逻辑;而methodOne内部调用this.methodTwo()时,this此时是Spy代理对象(不是原始的FooService实例),所以会被Mockito拦截,返回你预设的mock值。
这样就能完美满足你的需求:真实执行methodOne,同时mock掉它依赖的methodTwo返回值。
内容的提问来源于stack exchange,提问作者Richard
相关产品推荐
相关产品推荐

