您的位置:首页 >Mockito验证失败与内部依赖模拟解析
发布于2025-09-21 阅读(0)
扫一扫,手机访问

在单元测试中,我们通常希望隔离被测单元(System Under Test, SUT),只关注其自身的逻辑,而将其依赖项替换为模拟对象(Mock)。然而,当被测类(如示例中的Thing类)在执行其业务逻辑时,不是通过构造函数或Setter方法接收依赖,而是在其内部方法中直接创建这些依赖(如Thing的fun()方法内部创建ThingGrandParent,ThingGrandParent的GpFun()方法内部创建ThingParent),就会导致verify失败。
原始测试代码的问题在于:
简而言之,问题出在依赖的创建方式上:被测对象没有使用我们提供的模拟依赖,而是自行创建了真实的依赖。
为了解决上述问题,我们需要确保被测对象Thing在执行其业务逻辑时,能够使用我们预设的模拟对象。由于Thing内部通过fun()方法创建了ThingGrandParent,我们可以对Thing的fun()方法进行模拟,使其返回我们准备好的ThingGrandParent模拟对象。
以下是修正后的测试代码示例:
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
// 示例类定义 (与原文保持一致,此处省略以聚焦测试代码)
// @Getter @Setter public class SideThing { ... }
// public class ThingGrandParent { ... }
// public class ThingParent { ... }
// @Getter @Setter public class Thing { ... }
@RunWith(PowerMockRunner.class) // 如果需要PowerMock的其他高级功能,则保留
@PowerMockIgnore("javax.management.*")
// 准备所有涉及模拟或间谍的类,特别是那些最终的、静态的或构造函数被模拟的类
@PrepareForTest({Thing.class, SideThing.class, ThingGrandParent.class, ThingParent.class})
public class ThingTest {
@Mock
ThingParent mockThingParent; // 模拟ThingParent
@Mock
ThingGrandParent mockThingGrandParent; // 模拟ThingGrandParent
@Mock
SideThing mockSideThing; // 模拟SideThing
Thing spyThing; // Thing的间谍对象
// 使用MockitoRule替代 deprecated 的 MockitoAnnotations.initMocks
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Before
public void setup() {
// 创建Thing的间谍对象,这样可以调用真实方法,也可以模拟特定方法
spyThing = spy(new Thing());
// 不需要 MockitoAnnotations.initMocks(this);
}
@Test
public void testLogicWithMockedDependencies() throws Exception {
int expectedWeight = 4;
int inputSize = 4;
// 1. 模拟 spyThing.fun() 方法的行为
// 当 spyThing 的 fun() 方法被调用时,返回我们的 mockThingGrandParent 模拟对象。
// 使用 doReturn().when() 对 spy 对象进行方法模拟,这比 when().thenReturn() 更安全。
doReturn(mockThingGrandParent).when(spyThing).fun(anyInt());
// 2. 模拟 mockThingGrandParent.GpFun() 方法的行为
// 当 mockThingGrandParent 的 GpFun() 方法被调用时,返回我们的 mockThingParent 模拟对象。
doReturn(mockThingParent).when(mockThingGrandParent).GpFun(anyInt());
// 3. 模拟 mockThingParent.fetchSideThing() 方法的行为
// 当 mockThingParent 的 fetchSideThing() 方法被调用时,返回我们的 mockSideThing 模拟对象。
doReturn(mockSideThing).when(mockThingParent).fetchSideThing();
// 4. 模拟 mockSideThing.getWeight() 方法的行为
// 当 mockSideThing 的 getWeight() 方法被调用时,返回预期的权重值。
when(mockSideThing.getWeight()).thenReturn(expectedWeight);
// 执行被测方法
int result = spyThing.bLogic(inputSize);
// 验证结果
assertEquals(expectedWeight, result);
// 验证方法调用
// 验证 spyThing 的 fun 方法被调用了一次,参数为 inputSize
verify(spyThing, times(1)).fun(inputSize);
// 验证 mockThingGrandParent 的 GpFun 方法被调用了一次,参数为 inputSize
verify(mockThingGrandParent, times(1)).GpFun(inputSize);
// 验证 mockThingParent 的 fetchSideThing 方法被调用了一次
verify(mockThingParent, times(1)).fetchSideThing();
// 验证 mockSideThing 的 getWeight 方法被调用了一次
verify(mockSideThing, times(1)).getWeight();
}
}代码解析:
MockitoAnnotations.initMocks(this); 方法在Mockito 2.x版本后已被标记为弃用。对于JUnit 4和JUnit 5,有更现代和推荐的替代方案:
JUnit 4 (@Rule): 使用MockitoJUnit.rule()结合@Rule注解。这会自动初始化所有带有@Mock、@Spy或@InjectMocks注解的字段。
import org.junit.Rule;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
public class MyTest {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
MyDependency mockDependency;
// ...
}JUnit 5 (@ExtendWith): 使用@ExtendWith(MockitoExtension.class)注解。这是JUnit 5中扩展机制的一部分。
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class MyTest {
@Mock
MyDependency mockDependency;
// ...
}在示例代码中,我们采用了JUnit 4的@Rule方式。
@Spy和@InjectMocks都是Mockito中用于处理真实对象与模拟对象结合的注解,但它们的应用场景和目的不同。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
9