You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需抽象类:解决Mock新建对象thenReturn失效与Bean实例化异常

解决直接new对象导致Mock失效且兼容SpringBoot端到端测试的方案

问题背景

  • 初始实现:BSending构造函数直接通过new RandomNumGen(thresh)创建对象,单元测试中无法MockRandomNumGen,导致预期的aSender.printLine()未触发
  • 尝试方案:用抽象类Conf封装对象创建逻辑,单元测试可MockConf的newRandomNumGen()方法返回Mock实例,但SpringBoot端到端测试因抽象类无法实例化,抛出BeanInstantiationException

无抽象类的可行解决方案

方案1:直接采用构造注入改造BSending

核心思路是把RandomNumGen作为构造参数传入BSending,而非在类内部直接new,从根源上消除无法Mock的问题。

改造后的BSending类

public class BSending {
    private final RandomNumGen randomNumGen;
    private final Sender aSender;

    // 构造注入RandomNumGen,不再内部创建实例
    public BSending(int thresh, Sender aSender, RandomNumGen randomNumGen) {
        this.randomNumGen = randomNumGen;
        this.aSender = aSender;
    }

    // 业务方法示例
    public void doSend() {
        int num = randomNumGen.generate();
        if (num > 0) {
            aSender.printLine("Success");
        }
    }
}

单元测试(Mockito实现)

直接MockRandomNumGen实例,传入BSending构造即可完成测试:

@ExtendWith(MockitoExtension.class)
public class BSendingTest {
    @Mock
    private Sender mockSender;
    @Mock
    private RandomNumGen mockRandomNumGen;

    @Test
    public void testDoSend() {
        // 配置Mock返回值
        when(mockRandomNumGen.generate()).thenReturn(1);
        // 传入Mock实例创建BSending
        BSending bSending = new BSending(5, mockSender, mockRandomNumGen);
        
        bSending.doSend();
        // 验证预期方法被触发
        verify(mockSender).printLine("Success");
    }
}

端到端测试(SpringBootTest)

在Spring配置类中声明RandomNumGen和BSending的Bean,Spring会自动完成依赖注入,不会出现抽象类实例化问题:

@Configuration
public class AppConfig {
    @Bean
    public RandomNumGen randomNumGen() {
        // 可根据实际需求从配置文件读取阈值,这里用固定值示例
        return new RandomNumGen(10);
    }

    @Bean
    public BSending bSending(Sender sender, RandomNumGen randomNumGen) {
        return new BSending(10, sender, randomNumGen);
    }
}

之后直接使用@SpringBootTest即可正常运行端到端测试。

方案2:用普通工厂类封装对象创建

如果无法直接修改BSending的构造参数,可使用非抽象的工厂类封装RandomNumGen的创建逻辑,既保留封装性,又能兼容Spring实例化。

普通工厂类与改造后的BSending

// 非抽象的工厂类
public class RandomNumGenFactory {
    public RandomNumGen create(int thresh) {
        return new RandomNumGen(thresh);
    }
}

// 改造后的BSending
public class BSending {
    private final RandomNumGen randomNumGen;
    private final Sender aSender;

    public BSending(int thresh, Sender aSender, RandomNumGenFactory factory) {
        this.randomNumGen = factory.create(thresh);
        this.aSender = aSender;
    }

    // 业务方法同前
}

单元测试

Mock工厂类的create方法,让它返回Mock的RandomNumGen实例:

@ExtendWith(MockitoExtension.class)
public class BSendingTest {
    @Mock
    private Sender mockSender;
    @Mock
    private RandomNumGen mockRandomNumGen;
    @Mock
    private RandomNumGenFactory mockFactory;

    @Test
    public void testDoSend() {
        when(mockFactory.create(anyInt())).thenReturn(mockRandomNumGen);
        when(mockRandomNumGen.generate()).thenReturn(1);
        
        BSending bSending = new BSending(5, mockSender, mockFactory);
        bSending.doSend();
        
        verify(mockSender).printLine("Success");
    }
}

端到端测试

将工厂类声明为Spring Bean,Spring可正常实例化普通类,无异常:

@Configuration
public class AppConfig {
    @Bean
    public RandomNumGenFactory randomNumGenFactory() {
        return new RandomNumGenFactory();
    }

    @Bean
    public BSending bSending(Sender sender, RandomNumGenFactory factory) {
        return new BSending(10, sender, factory);
    }
}

方案选择建议

  • 优先选方案1:完全符合依赖注入原则,代码更简洁,测试维护成本更低
  • 方案2适合:无法修改BSending构造参数的遗留代码场景,保留了对象创建的封装逻辑

内容的提问来源于stack exchange,提问作者user5739619

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 21:23:14