modify/refactor 支持集成测试的产品代码的正确方法是什么?

What is the correct way here to modify/refactor product code to support integration testing?

下面给出了我的产品代码的本质和简化表示。 这就像一个完成工作流程的算法。每个步骤(私有方法)都会导致数据库中记录的更改。即使是外部工具调用也会通过另一种回调反馈机制更新数据库。

void IWorkFlow.PerformBusinessWorkflowX(Input input) 
{

    PreparePreRequisiteData();

    DoTask_A();

    TriggerExternalToolTo_DoTask_B();   /* external tool invocation */
}

我的一位同事想为此编写一个集成测试用例,但不想包含外部工具调用。他的目标是在集成级别测试其余的 BusinessWorkFlow 逻辑,但不包括外部工具。不是使用模拟的单元测试,而是集成测试,他在其中验证数据库预测试和 post 测试的状态。为了达到目的他把代码修改成这样

void IWorkFlow.PerformBusinessWorkflowX(Input input, bool isTriggerExternalTool) 
{

    PreparePreRequisiteData();

    DoTask_A();

    if(isTriggerExternalTool) 
    {
        TriggerExternalToolTo_DoTask_B();   /* external tool invocation */
    }
}

但是,我对这种重构方式不太满意。由于外部工具的调用是业务工作流的 unseparable/integral 部分,我宁愿不将产品代码修改为具有指示工具调用是可选的布尔标志。

我认为更好的解决方案(但由于它的性质而丑陋(?))将坚持使用原始方法而不需要传入布尔标志依赖项。相反,决定调用该工具来支持嵌入在 TriggerExternalToolTo_DoTask_B() 方法中的测试。或者甚至通过调用它 TryTriggerExternalToolTo_DoTask_B().

有点像,

private void TryTriggerExternalToolTo_DoTask_B()
{
    //will be false for integration tests. Any other value then invoke. 
    if(ConfigurationManager.AppSettings["InvokeTool"] != "false") 
    {

    }
}

我也不赞成将 IWorkFlow.PerformBusinessWorkflowX(Input input) 方法分解为两部分(一部分执行前两个步骤,第二部分仅执行通过不同接口方法公开的工具调用), 只是为了产品代码的灵活性和支持测试代码。还因为所有步骤都属于单个工作流程(算法)。

问题

1) 我说在产品代码中引入布尔标志只是为了支持测试可能不是最佳实践,我说错了吗? (有些人告诉我这就是可测试性设计。我希望这不是可测试性设计的实际含义)

2) 将调用逻辑下推到 TryTriggerExternalToolTo_DoTask_B() 并依靠 appsetting 来拯救这个看似丑陋的解决方案是更好的解决方案吗?

3)看到我不想为了灵活而分解方法(扩展接口),请问上面的问题有更好的解决方案吗?

PS - 孤陋寡闻请指正,并提供开放的技术建议

不,我不会为此修改原型。呃,我讨厌测试人员胡闹我优雅的 class 设计。

处理此问题的适当方法是 inject 执行外部函数的 class。如果注入的 class 为空,则跳过它。通过这种方式,测试人员可以使用 AutoFac 或您正在使用的任何 DI 工具包配置他的测试;他可以打开它,关闭它,甚至可以用他自己的 IExternalToolInvoker class 代替,后者仅检查输入(在我的示例中为 abc)和确保它们是正确的。

class WorkflowExecutor : IWorkflow
{
    IExternalToolInvoker _invoker;

    public WorkflowExecutor(IExternalToolInvoker invoker)
    {
        _invoker = invoker;
    }

    public void Execute(IInput input)
    {
        PreparePrerequisiteData();
        DoTask_A();

        if (_invoker != null) invoker.TryTriggerExternalTool(a, b, c);
    }
}

我也喜欢@JohnWu 的回答。需要考虑的另一件事:测试人员 modification/request 可能表明您的 class 做得太多了。依赖注入解决方案将是一种分解功能的方法。