如何模拟继承方法调用?Mockito模拟失效问题求助
问题分析与解决方案
核心问题
模拟失效的根本原因是:待测试的collectData方法内部直接通过new DataB(dbCon)、new DataC(dbCon)创建实例,你在测试类中@Mock出来的dataB/dataC对象,和方法内新建的实例完全是两个独立对象,Mockito的模拟规则根本作用不到方法内的真实实例上,自然会执行真实的数据库逻辑触发异常。
解决方案
方案一:重构代码(推荐,符合依赖注入原则)
修改待测试方法,不在内部直接new对象,而是通过外部传入实例或注入工厂类,让测试时可以控制DataB/DataC的实例创建。
方式1:参数注入实例
重构后的待测试方法:
public TypeA collectData(Connection dbCon, String id, DataB dataB, DataC dataC){ TypeA a = new TypeA(); a.setTime(LocalDateTime.now()); a.setId(id); TypeB b = dataB.getData(id); a.setB(b); List<TypeC> c = dataC.getDataList(id); a.getC().addAll(c); return a; }
对应测试代码:
@ExtendWith(MockitoExtension.class) class DataTest { @Mock private Connection c; @Mock private DataB dataB; @Mock private DataC dataC; @Test void testTypeA(){ // 模拟方法返回值 when(dataB.getData("123")).thenReturn(new TypeB()); List<TypeC> cL = new ArrayList<>(); cL.add(new TypeC()); when(dataC.getDataList("123")).thenReturn(cL); // 传入Mock对象执行测试 TypeA a = collectData(c, "123", dataB, dataC); assertThat(a.getId(), is(equalTo("123"))); // 其他断言逻辑 } }
方式2:工厂模式封装实例创建
如果不想修改方法参数,可以用工厂类封装DataB/DataC的创建:
// 定义工厂接口 public interface DataFactory { DataB createDataB(Connection dbCon); DataC createDataC(Connection dbCon); } // 真实工厂实现 public class DefaultDataFactory implements DataFactory { @Override public DataB createDataB(Connection dbCon) { return new DataB(dbCon); } @Override public DataC createDataC(Connection dbCon) { return new DataC(dbCon); } }
修改待测试方法(假设所在类注入了DataFactory):
private final DataFactory dataFactory; // 构造注入工厂 public YourClass(DataFactory dataFactory) { this.dataFactory = dataFactory; } public TypeA collectData(Connection dbCon, String id){ TypeA a = new TypeA(); a.setTime(LocalDateTime.now()); a.setId(id); DataB dataB = dataFactory.createDataB(dbCon); TypeB b = dataB.getData(id); a.setB(b); DataC dataC = dataFactory.createDataC(dbCon); List<TypeC> c = dataC.getDataList(id); a.getC().addAll(c); return a; }
对应测试代码:
@ExtendWith(MockitoExtension.class) class DataTest { @Mock private Connection c; @Mock private DataB dataB; @Mock private DataC dataC; @Mock private DataFactory dataFactory; @Test void testTypeA(){ // 模拟工厂返回Mock实例 when(dataFactory.createDataB(c)).thenReturn(dataB); when(dataFactory.createDataC(c)).thenReturn(dataC); // 模拟方法返回值 when(dataB.getData("123")).thenReturn(new TypeB()); List<TypeC> cL = new ArrayList<>(); cL.add(new TypeC()); when(dataC.getDataList("123")).thenReturn(cL); TypeA a = collectData(c, "123"); assertThat(a.getId(), is(equalTo("123"))); } }
方案二:使用PowerMockito拦截new操作(不推荐,仅应急)
如果无法重构原有代码,可以用PowerMockito拦截方法内的new操作,强制返回Mock对象。
Maven依赖配置
<dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>2.0.9</version> <scope>test</scope> </dependency>
测试代码
@RunWith(PowerMockRunner.class) @PrepareForTest({YourCollectDataClass.class}) // 替换为collectData方法所在的类名 class DataTest { @Mock private Connection c; @Mock private DataB dataB; @Mock private DataC dataC; @Test void testTypeA() throws Exception { // 拦截new DataB(dbCon),返回Mock实例 PowerMockito.whenNew(DataB.class).withArguments(c).thenReturn(dataB); // 拦截new DataC(dbCon),返回Mock实例 PowerMockito.whenNew(DataC.class).withArguments(c).thenReturn(dataC); // 模拟方法返回值 when(dataB.getData("123")).thenReturn(new TypeB()); List<TypeC> cL = new ArrayList<>(); cL.add(new TypeC()); when(dataC.getDataList("123")).thenReturn(cL); TypeA a = collectData(c, "123"); assertThat(a.getId(), is(equalTo("123"))); } }
内容的提问来源于stack exchange,提问作者StarMcSnow
相关产品推荐
相关产品推荐

