修改/重构产品代码以支持集成测试的正确方法是啥?

Posted

技术标签:

【中文标题】修改/重构产品代码以支持集成测试的正确方法是啥?【英文标题】:What is the correct way here to modify/refactor product code to support integration testing?修改/重构产品代码以支持集成测试的正确方法是什么? 【发布时间】:2017-06-04 13:39:03 【问题描述】:

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

void IWorkFlow.PerformBusinessWorkflowX(Input input) 


    PreparePreRequisiteData();

    DoTask_A();

    TriggerExternalToolTo_DoTask_B();   /* external tool invocation */

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

void IWorkFlow.PerformBusinessWorkflowX(Input input, bool isTriggerExternalTool) 


    PreparePreRequisiteData();

    DoTask_A();

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

但是,我对这种重构风格并不满意。由于外部工具的调用是业务工作流程中不可分割/不可分割的一部分,因此我宁愿不将产品代码修改为具有指示工具调用是可选的布尔标志。

我认为更好的解决方案(但丑陋(?)因为它的性质)是坚持使用原始方法而不需要传入布尔标志依赖项。相反,决定调用该工具来支持测试嵌入到 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) 方法分解为两部分(一部分执行前 2 个步骤,第二部分仅执行通过不同接口方法公开的工具调用),只是为了为了产品代码的灵活性和支持测试代码。也因为所有步骤都属于单个工作流(算法)。

问题

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

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

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

PS - 如果我不了解,请纠正我,并提供开放的技术建议。

【问题讨论】:

根据您在这里所说的,改变设计是我认为的“最佳”解决方案。我没有发现 1 或 2 比另一个特别好。他们只是在不同的方面很糟糕。至于“可测试性设计”,不,我当然不会认为 1 是一个例子。 【参考方案1】:

不,我不会为此修改原型。呃,我讨厌测试人员嘲笑我优雅的类设计。

处理这个问题的适当方法是inject一个执行外部函数的类。如果注入的类为空,则跳过它。这样,测试人员可以使用 AutoFac 或您正在使用的任何 DI 工具包配置他的测试;他可以打开、关闭它,甚至替换他自己的 IExternalToolInvoker 类,该类仅检查输入(在我的示例中为 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);
    

【讨论】:

更好的办法是禁止空调用者,而是提供一个实现接口并且什么都不做的空对象。那就没有特殊情况了。 @John Wu,我在发布问题后上床睡觉时也恰好想到了这种DI的可能性。早上醒来就听到你的回答。看起来这是最好的方法,简单的 DI。以及 Eric 在 cmets 中提出的改进建议。【参考方案2】:

我也喜欢@JohnWu 的回答。要考虑的另一件事:测试人员的修改/请求可能表明您的班级做得太多。依赖注入解决方案将是一种将功能分开的方法。

【讨论】:

以上是关于修改/重构产品代码以支持集成测试的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

实验二

idea代码提示修改不了

码云 Jenkins 插件重构出炉 —— 码云的持续集成利器

在软件测试中UT,IT,ST,UAT分别是啥意思

中文版编写php代码的工具有那些?

基于 BDD 理论的 Nebula 集成测试框架重构(上篇)