
本文讲解如何通过构造函数注入替代 new 实例化,使 randomnumgen 可被 mockito 正确 mock,从而避免因内部 new 创建对象导致的测试失效问题,并兼容 spring boot 集成测试。
本文讲解如何通过构造函数注入替代 new 实例化,使 randomnumgen 可被 mockito 正确 mock,从而避免因内部 new 创建对象导致的测试失效问题,并兼容 spring boot 集成测试。
在单元测试中,若被测类(如 BSending)内部通过 new RandomNumGen(thresh) 直接实例化依赖对象,该对象将脱离测试框架控制——即使你在测试中 @Mock 了 RandomNumGen,实际运行时调用的仍是全新创建的真实实例,导致 when(...).thenReturn(...) 失效,verify 也自然无法命中。
根本原因在于:Mockito 只能拦截被显式注入或引用的 mock 对象行为,无法影响 new 出来的独立实例。
✅ 正确解法是:将 RandomNumGen 作为构造参数注入,而非内部 new 创建。这既符合依赖倒置原则,又天然支持测试与生产环境的一致性。
✅ 推荐重构方案(无需抽象类)
// 保持 RandomNumGen 不变(非抽象、可实例化)
public class RandomNumGen {
private final int thresh;
public RandomNumGen(int thresh) {
this.thresh = thresh;
}
public boolean isChosen() {
return isChosenX(this.thresh, new Random());
}
public boolean isChosenX(int thresh, Random random) {
// 示例逻辑:生成 [0,100) 的随机数,小于 thresh 则返回 true
return random.nextInt(100) < thresh;
}
}// 修改 BSending:移除 new,改用构造注入
public class BSending {
protected final Sender aSender;
private final RandomNumGen randomNumGen; // ← 由外部提供,不再 new
@Autowired
public BSending(Sender sender, RandomNumGen randomNumGen) {
this.aSender = sender;
this.randomNumGen = randomNumGen;
}
public void sendTask() {
if (randomNumGen.isChosen()) {
aSender.printLine();
}
}
}// 单元测试:直接注入 mock 实例,简洁可靠
@SpringBootTest
class BSendingTest {
@Mock
private Sender aSender;
@Mock
private RandomNumGen randomNumGen;
@InjectMocks
private BSending bSending;
@Test
void testSendTaskWhenIsChosenReturnsTrue() {
// Arrange
when(randomNumGen.isChosen()).thenReturn(true);
// Act
bSending.sendTask();
// Assert
verify(aSender, times(1)).printLine();
}
}⚠️ 注意事项:
@InjectMocks会自动将@Mock标注的字段注入到被测对象中(需确保构造器参数名/类型匹配);若
RandomNumGen在 Spring 上下文中需按阈值动态配置(如从配置文件读取),可定义@Bean工厂方法:@Configuration public class AppConfig { @Value("${app.threshold:25}") private int threshold; @Bean public RandomNumGen randomNumGen() { return new RandomNumGen(threshold); } }此方案完全避免抽象类,
@SpringBootTest(classes = {BSending.class})可正常启动,Spring 会自动装配RandomNumGenBean(前提是它已声明为@Component或通过@Bean注册)。
✅ 衍生优势
-
测试更轻量:无需
@Mock额外中间层(如Conf),减少间接依赖; - 生产更清晰:依赖关系一目了然,便于 DI 容器管理与 AOP 增强;
-
配置更灵活:阈值可通过
application.yml统一管理,无需硬编码或文件 I/O; -
端到端测试无阻:
@SpringBootTest直接加载真实RandomNumGenBean,无需绕过抽象类限制。
综上,将“new 创建”改为“构造注入”是最直接、最符合 Spring 和测试最佳实践的解决方案——它消除了测试盲区,提升了代码可维护性,且无需引入抽象类等复杂设计。


















