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

方法内通过Factory方法创建类时的Mock测试最佳实践咨询

核心结论

你担心「仅为测试抽象接口属于过度设计」是误区,这类依赖外部创建对象的场景,依赖注入+工厂接口是行业通用的最佳实践,完全不算过度设计,同时你也可以根据项目规模选择更轻量的改造方案。


可选方案及适用场景

1. 工厂接口注入(长期项目最推荐)

静态工厂的核心问题是硬编码了Car类和工厂的耦合关系,抽象接口的改动成本极低,同时还能提升生产代码的可维护性:
首先定义工厂接口:

public interface IEngineTypeFactory
{
    EngineType GetEngineType(CarType carType);
}

原静态工厂改为接口实现类:

public class EngineTypeFactory : IEngineTypeFactory
{
    public EngineType GetEngineType(CarType carType)
    {
        return carType switch
        {
            CarType.Petrol => EngineType.Petrol,
            CarType.Electric => EngineType.Electric,
            _ => throw new ArgumentOutOfRangeException(nameof(carType))
        };
    }
}

Car类改造为构造函数注入工厂实例:

public class Car
{
    private readonly IEngineTypeFactory _engineFactory;

    public Car(IEngineTypeFactory engineFactory)
    {
        _engineFactory = engineFactory;
    }

    public void BuildCar(CarType carType)
    {
        var engineType = _engineFactory.GetEngineType(carType);
        var engineInsertionResult = InsertEngine(engineType);
        if (!engineInsertionResult.IsRunning)
        {
            throw new OhNoEngineFailedException("Engine Failure");
        }
    }

    private EngineInsertionResult InsertEngine(EngineType engineType)
    {
        var ignitionResult = engineType.TurnOn();
        return new EngineInsertionResult() 
        {
            IsRunning = ignitionResult.IsEngineRunning
        };
    }
}

测试时直接Mock IEngineTypeFactory的GetEngineType方法,返回自定义的EngineType实例,控制TurnOn()的返回值即可覆盖IsRunning为true/false的所有场景。
优势说明:这个改造不止是为了测试,后续新增混动等车型时,只需要修改工厂实现,不需要动Car类的代码,完全符合开闭原则,额外收获了生产代码的可扩展性。

2. 静态工厂可配置委托(小项目轻量方案)

如果项目规模极小,不想引入依赖注入框架,也可以给静态工厂加测试入口,改造成本几乎为零:

public static class EngineTypeFactory
{
    // 测试时可替换该委托自定义返回值
    public static Func<CarType, EngineType> GetEngineTypeImpl { get; set; } = carType =>
    {
        return carType switch
        {
            CarType.Petrol => EngineType.Petrol,
            CarType.Electric => EngineType.Electric,
            _ => throw new ArgumentOutOfRangeException(nameof(carType))
        };
    };

    public static EngineType GetEngineType(CarType carType) => GetEngineTypeImpl(carType);
}

注意事项:该方案的静态委托是全局生效的,多线程并行测试时会出现冲突,仅适合单线程测试的小型项目。

3. 直接测试公开行为(无外部依赖场景最优)

如果EngineType的TurnOn()方法是纯逻辑实现,没有外部IO、配置读取等依赖,完全不需要修改现有代码,直接构造对应场景的CarType参数,通过EngineType的测试构造参数控制TurnOn()的返回值即可完成测试,改造成本最低。


补充说明

你提到的将EngineType作为BuildCar方法入参的方案也是合理的,适合EngineType由上层业务逻辑生成的场景;如果确实需要在BuildCar内部根据CarType动态生成EngineType,工厂方案是更符合单一职责的选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 13:15:01