如何使用Mockito和TestNG模拟方法内部创建的对象
问题:Mock方法内部创建的依赖类实例,使其返回指定值
业务代码
public class MyClass { public boolean myMethod() { DependentClass dependentObject = new DependentClass(); return dependentObject.dependentMethod(); } } class DependentClass { public boolean dependentMethod() { // Original behavior return true; } }
无效的测试代码
import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class MyClassTest { @Mock private DependentClass dependentClassMock; @InjectMocks private MyClass myClass; @BeforeMethod public void setUp() { MockitoAnnotations.initMocks(this); } @Test public void testMyMethod() { // Arrange Mockito.when(dependentClassMock.dependentMethod()).thenReturn(false); // Act boolean result = myClass.myMethod(); // Assert Assert.assertFalse(result); } }
问题核心:myMethod内直接通过new DependentClass()创建实例,导致@InjectMocks注入的Mock对象完全没被用到,测试结果始终返回true。需要实现Mock该内部实例,且尽可能少修改原代码。
方案1:小幅度重构MyClass(推荐,符合依赖注入原则)
仅新增一个构造方法用于测试注入Mock,保留原无参构造兼容现有业务逻辑,无侵入性:
修改后的MyClass:
public class MyClass { private DependentClass dependentObject; // 保留原无参构造,不影响现有调用 public MyClass() { this.dependentObject = new DependentClass(); } // 新增构造方法,用于测试注入Mock实例 public MyClass(DependentClass dependentObject) { this.dependentObject = dependentObject; } public boolean myMethod() { return dependentObject.dependentMethod(); } }
对应的有效测试代码:
import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class MyClassTest { @Mock private DependentClass dependentClassMock; private MyClass myClass; @BeforeMethod public void setUp() { MockitoAnnotations.initMocks(this); // 通过带参构造注入Mock实例 myClass = new MyClass(dependentClassMock); } @Test public void testMyMethod() { Mockito.when(dependentClassMock.dependentMethod()).thenReturn(false); boolean result = myClass.myMethod(); Assert.assertFalse(result); } }
方案2:使用Mockito PowerMock(无需修改原代码)
如果完全不想改动业务代码,可以用PowerMock拦截类的构造行为,强制返回Mock实例:
测试代码:
import org.mockito.Mock; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.testng.PowerMockTestCase; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @PrepareForTest(MyClass.class) // 告知PowerMock需要修改MyClass的字节码 public class MyClassTest extends PowerMockTestCase { @Mock private DependentClass dependentClassMock; private MyClass myClass; @BeforeMethod public void setUp() { myClass = new MyClass(); } @Test public void testMyMethod() throws Exception { // 拦截DependentClass的无参构造,返回Mock实例 PowerMockito.whenNew(DependentClass.class).withNoArguments().thenReturn(dependentClassMock); // 指定Mock方法的返回值 Mockito.when(dependentClassMock.dependentMethod()).thenReturn(false); boolean result = myClass.myMethod(); Assert.assertFalse(result); // 验证构造方法被调用一次 PowerMockito.verifyNew(DependentClass.class).withNoArguments(); } }
注意:PowerMock通过修改字节码实现拦截,会增加测试复杂度,仅在无法重构业务代码时使用。
内容的提问来源于stack exchange,提问作者TechGoose
相关产品推荐
相关产品推荐

