TDD:在重构代码的同时破坏所有现有的测试用例

2023-12-26

我已经开始在我的项目中遵循 TDD。但自从我开始,即使读了一些文章,我也很困惑,因为发展速度变慢了。每当我重构代码时,我都需要更改之前编写的现有测试用例,否则它们将开始失败。

下面是我最近重构的一个类的例子:

public class SalaryManager
{
    public string CalculateSalaryAndSendMessage(int daysWorked, int monthlySalary)
    {
        int salary = 0, tempSalary = 0;
        if (daysWorked < 15)
        {
            tempSalary = (monthlySalary / 30) * daysWorked;
            salary = tempSalary - 0.1 * tempSalary;
        }
        else
        {
            tempSalary = (monthlySalary / 30) * daysWorked;
            salary = tempSalary + 0.1 * tempSalary;
        }

        string message = string.Empty;
        if (salary < (monthlySalary / 30))
        {
            message = "Salary cannot be generated. It should be greater than 1 day salary.";
        }
        else
        {
            message = "Salary generated as per the policy.";
        }

        return message;
    }
}

但现在我用一种方法做很多事情,所以为了遵循单一职责原则(SRP),我将其重构为如下所示:


public class SalaryManager
{
    private readonly ISalaryCalculator _salaryCalculator;        
    private readonly SalaryMessageFormatter _messageFormatter;
    public SalaryManager(ISalaryCalculator salaryCalculator, ISalaryMessageFormatter _messageFormatter){
        _salaryCalculator = salaryCalculator;
        _messageFormatter = messageFormatter;
    }

    public string CalculateSalaryAndSendMessage(int daysWorked, int monthlySalary)
    {
        int salary = _salaryCalculator.CalculateSalary(daysWorked, monthlySalary);
        string message = _messageFormatter.FormatSalaryCalculationMessage(salary);

        return message;
    }
}

public class SalaryCalculator
{
    public int CalculateSalary(int daysWorked, int monthlySalary)
    {
        int salary = 0, tempSalary = 0;
        if (daysWorked < 15)
        {
            tempSalary = (monthlySalary / 30) * daysWorked;
            salary = tempSalary - 0.1 * tempSalary;
        }
        else
        {
            tempSalary = (monthlySalary / 30) * daysWorked;
            salary = tempSalary + 0.1 * tempSalary;
        }
        return salary;
    }
}

public class SalaryMessageFormatter
{
    public string FormatSalaryCalculationMessage(int salary)
    {
        string message = string.Empty;
        if (salary < (monthlySalary / 30))
        {
            message = "Salary cannot be generated. It should be greater than 1 day salary.";
        }
        else
        {
            message = "Salary generated as per the policy.";
        }
        return message;
    }
}

这可能不是最好的例子。但要点是,一旦我进行了重构,我为SalaryManager开始失败,我不得不使用模拟来修复它们。

这种情况在读取时间场景中经常发生,并且开发时间也随之增加。我不确定我是否以正确的方式进行 TDD。请帮助我理解。


每当我重构代码时,我都需要更改之前编写的现有测试用例,因为它们会开始失败。

这肯定表明出现了问题。重构的流行定义是这样的this https://refactoring.com/

重构是一种严格的技术,用于重组现有代码体,改变其内部结构而不改变其外部行为。

进行单元测试的部分目的是单元测试正在评估实现的外部行为。失败的单元测试表明实现更改以某种方式改变了外部可观察的行为。

在这种特殊情况下,您似乎更改了 API - 具体来说,您删除了默认构造函数,该构造函数是用于创建实例的 API 的一部分SalaryManager;这不是“重构”,而是向后的破坏性改变。

在重构时引入新的协作者并没有什么问题,但您应该以不破坏当前 API 契约的方式这样做。

public class SalaryManager
{
    public SalaryManager(ISalaryCalculator salaryCalculator, ISalaryMessageFormatter _messageFormatter){
        _salaryCalculator = salaryCalculator;
        _messageFormatter = messageFormatter;
    }

    public SalaryManager() {
        this(new SalaryCalculator(), new SalaryMessageFormatter())
    }

where SalaryCalculator and SalaryMessageFormatter应该是产生与您最初相同的可观察行为的实现。

当然,有时我们需要引入向后的突破性改变。然而,“重构”并不是适合这种情况的工具。在许多情况下,您可以分几个阶段实现您想要的结果:首先使用新测试扩展您的 API(重构以删除与现有实现的重复),然后删除评估旧 API 的测试,最后删除旧 API。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TDD:在重构代码的同时破坏所有现有的测试用例 的相关文章

  • WindowsError:[错误 126] 使用 ctypes 加载操作系统时

    python代码无法在Windows 7平台上运行 def libSO lib ctypes cdll LoadLibrary ConsoleApplication2 so lib cfoo2 1 3 当我尝试运行它时 得到来自python
  • 如何在 VC++ CString 中验证有效的整数和浮点数

    有人可以告诉我一种有效的方法来验证 CString 对象中存在的数字是有效整数还是浮点数吗 Use tcstol http msdn microsoft com en us library w4z2wdyc aspx and tcstod
  • 使用 CMake 时如何导出 Emscripten 中的 C 函数

    In 本教程 https emscripten org docs porting connecting cpp and javascript Interacting with code html interacting with code
  • 在 CPP 类中将 C 函数声明为友元

    我需要在 C 函数中使用类的私有变量 我正在做这样的事情 class Helper private std string name public std getName return name friend extern C void in
  • 如何将 .txt 文件中的数据转换为 xml? C#

    我在一个文本文件中有数千行数据 我想通过将其转换为更容易搜索的内容来轻松搜索 我希望 XML 或其他类型的大型数据结构 尽管我不确定它是否是最好的对于我的想法 每行的数据如下所示 第 31 册 托马斯 乔治 32 34 154 每本书都不是
  • cpp.react库的C++源代码中奇怪的“->* []”表达式

    这是我在文档中找到的 C 片段cpp react 库 https github com schlangster cpp react implicit parallelism auto in D MakeVar 0 auto op1 in g
  • 语音识别编程问题入门

    所以 你们可能都看过 钢铁侠 其中托尼与一个名为贾维斯的人工智能系统进行交互 演示剪辑here http www youtube com watch v Go8zsh1Ev6Y 抱歉 这是广告 我非常熟悉 C C 和 Visual Basi
  • Eigen 和 OpenMP:由于错误共享和线程开销而没有并行化

    系统规格 Intel Xeon E7 v3 处理器 4 插槽 16 核 插槽 2 线程 核心 Eigen 系列和 C 的使用 以下是代码片段的串行实现 Eigen VectorXd get Row const int j const int
  • 什么是空终止字符串?

    它与什么不同标准 字符串 http www cplusplus com reference string string 字符串 实际上只是一个数组chars 空终止字符串是指其中包含空字符的字符串 0 标记字符串的结尾 不一定是数组的结尾
  • 如何使用 x64 运行 cl?

    我遇到了和这里同样的问题致命错误 C1034 windows h 未设置包含路径 https stackoverflow com questions 931652 fatal error c1034 windows h no include
  • 从 C# 使用 Odbc 调用 Oracle 包函数

    我在 Oracle 包中定义了一个函数 CREATE OR REPLACE PACKAGE BODY TESTUSER TESTPKG as FUNCTION testfunc n IN NUMBER RETURN NUMBER as be
  • 在 C# 中为父窗体中的子窗体控件添加事件处理程序

    我有两种形式 一种是带有按钮和文本框的父表单 单击该按钮时 将打开一个对话框 该子窗体又包含一个文本框和一个按钮 现在我想要的是 每当子表单文本框中的文本更改时 父表单文本框中的文本会自动更改 为了获得这个 我所做的是 Form3 f3 n
  • memcpy/memmove 到联合成员,这是否设置“活动”成员?

    重要说明 一些评论者似乎认为我是从工会抄袭的 仔细看memcpy 它从普通旧地址复制uint32 t 它不包含在联合中 另外 我正在复制 通过memcpy 到工会的特定成员 u a16 or u x in a union 不直接到整个联盟本
  • 将 Word 转换为 PDF - 禁用“保存”对话框

    我有一个用 C 编写的 Word 到 PDF 转换器 除了一件事之外 它工作得很好 有时 在某些 Word 文件上 后台会出现一条消息保存源文件中的更改 gt 是 否 取消 但我没有对源文件进行任何更改 我只想从 Word 文件创建 PDF
  • C++ 对象用 new 创建,用 free() 销毁;这有多糟糕?

    我正在修改一个相对较大的 C 程序 不幸的是 并不总是清楚我之前的人使用的是 C 还是 C 语法 这是在一所大学的电气工程系 我们 EE 总是想用 C 来做所有事情 不幸的是 在这种情况下 人们实际上可以逃脱惩罚 但是 如果有人创建一个对象
  • 使动态创建的链接标签在 Winforms 中可点击

    我正在制作一个程序 允许用户单击由动态链接标签创建的公司名称 在我想知道如何做到这一点之前 我从未在 C 中使用过链接标签 可为特定用户生成的业务数量各不相同 因此每个用户的链接标签数量并不相同 然后我想捕获业务 ID 以进行 Json 调
  • 将 Lambda 表达式树与 IEnumerable 结合使用

    我一直在尝试了解有关使用 Lamba 表达式树的更多信息 因此我创建了一个简单的示例 这是代码 如果作为 C 程序粘贴到 LINQPad 中 它可以工作 void Main IEnumerable
  • Visual Studio 2015 - Web 项目上缺少共享项目参考选项卡

    我从 MSDN 订阅升级到 Visual Studio 2015 因为我非常兴奋地阅读有关共享项目的信息 当我们想要做的只是重用代码时 不再需要在依赖项中管理 21382 个 nuget 包 所以我构建了一个测试共享项目 其中包含一些代码
  • 是否允许全局静态标识符以单个 _ 开头?

    换句话说 可能static 文件范围 全局变量恰好以一个下划线开头 而不会产生与 C 实现发生名称冲突的可能性 https www gnu org software libc manual html node Reserved Names
  • MySqlConnectionStringBuilder - 使用证书连接

    我正在尝试连接到 Google Cloud Sql 这是一个 MySql 解决方案 我能够使用 MySql Workbench 进行连接 我如何使用 C 连接MySqlConnectionStringBuilder 我找不到提供这三个证书的

随机推荐

  • 在公共存储库中隐藏密钥

    我正在开发一个开源 JavaScript 应用程序 我正在尝试与第三方 API 特别是 github 进行交互 我试图将整个应用程序保留在客户端 所以我真的没有服务器可以回退或存储隐藏文件 作为 OAuth 流程的一部分 我需要提供为我的
  • 时间选择器显示时间为 4:7 而不是 04:07

    我有一个时间选择器功能 可以在 EditText 中设置时间 但它显示的格式不合适 例如 04 07pm 显示为 4 7 当时间中的数字小于 10 时 它会自动删除 0 请帮帮我 我的代码是 if v btnTimePicker1 Proc
  • 选择使用 IIS 的 WCF 服务托管或使用 Windows 服务的自托管

    因为我们想要使用 Asp Net MVC 3 开发一个新产品 这里的关键点是我们通过 WCF Web 服务与数据库对话的所有内容 我们正处于需要决定是在 IIS 中托管 WCF Web 服务还是使用 Windows 服务自托管的阶段 我提出
  • 带有滚动条的文本区域的圆角

    在我的网站上我有一个 htmltextarea框中有大量文本 因此它有一个滚动条 好吧 我想在我的文本区域上有圆角 但是滚动条看起来很糟糕 这是我的 HTML 片段
  • 变量名长度对 C# 性能重要吗?

    我一直想知道在 WinForms C 中使用长描述性变量名称是否对性能有影响 我问这个问题是因为在 AutoIt v3 解释语言 中提出了具有短名称的变量 例如aa代替veryLongVariableName速度要快得多 当程序大于 5 行
  • 弹簧接线,单例与原型

    刚刚阅读了 spring 的内容 当在 spring 中使用 DI 时 如果将 bean 设置为单例 默认 则将 分配 该类的单个实例 而原型每次都会强制创建一个新实例 每次容器分配相同的实例会产生什么后果 这是否意味着会有共享状态 也就是
  • 无法使用 Excel Interop 安排程序

    我用 C NET 开发了一个控制台程序 它使用 Excel Interop 如果我从命令行运行该程序 则该程序在我的开发计算机和 Windows Server 2008 中运行得很好 当我尝试安排任务每天运行时 我收到了这个恼人的互操作错误
  • ARM/neon memcpy 针对*未缓存*内存进行了优化?

    我使用的是基于 Xilinx Zynq 7000 ARM 的 SoC 我正在努力处理 DMA 缓冲区 需要帮助映射 Xilinx ARM SoC Zynq 7000 上的预留 可缓存 DMA 缓冲区 https stackoverflow
  • 通过 using 块使用 Process [重复]

    这个问题在这里已经有答案了 可能的重复 如果我不关闭 C 控制台应用程序中的 System Diagnostics Process 会发生什么情况 https stackoverflow com questions 185314 what
  • 即使 contentSize 大于框架并且在设置内容大小之前添加子视图,scrollView 也不起作用

    void viewDidLoad super viewDidLoad self navigationController navigationBar translucent YES self navigationController nav
  • C++ 计算器跳过 Else 语句

    我正在用 C 制作一个简单的计算器 然而 该程序并没有完全按照应有的方式运行 运行时 trig if 语句执行良好 但是基本算术 else 语句不起作用 我确定代码没有执行 else 语句 并且想知道如何修复它 else 语句中的代码工作正
  • Symfony2:成功登录事件后,执行一组操作

    我需要在用户成功登录后执行一组操作 这包括从数据库加载数据并将其存储在会话中 实现这一点的最佳方法是什么 您可以添加一个监听器security interactive login event 像这样附加你的听众 在此示例中 我还将安全上下文
  • 如何查看上次收到的推送中修改/添加/删除的所有文件?

    命令git show pretty format name status bd61ad98将显示上次提交中修改 添加 删除的所有文件的列表 其中bd61ad98是提交 ID 输出如下所示 trusktr rocketship express
  • 使用 RandomAccessFile 创建文件时出现 java.io.FileNotFoundException

    当我尝试使用 RandomAccessFile 创建文件时遇到 FileNotFoundException RandomAccessFile file new RandomAccessFile test jpg rw 我现在不知道如何解决这
  • qt Creator 调试速度慢

    我正在 Windows 7 64 位上运行基于 Qt 4 7 4 的 Qt Creator 2 3 0 当我从 Qt Creator ctrl r 运行该应用程序时 一切都运行良好 然而 当我调试 F5 时 执行每一行都需要很长时间 有时执
  • 修改ISAPI和CGI扩展

    我的 IIS 服务器有问题 如何使用 C 语言修改 ISAPI 元素 例如 ASP net V4 0 限制为 不允许 我想设置为 允许 如下图所示 我可以使用此代码添加元素 但我无法修改 using System using System
  • JSON - 斜杠不转义

    这是我的 PHP Json 脚本
  • 如何禁用警告数据表警告从行的数据源请求未知参数

    我的数据表中有两行 如下所示 tr td td td td td td tr tr td td tr 这些行对于数据表上的每条记录都会重复 正如您可以想象的那样 我总是收到此错误 并且只需要禁用它 除非您想弄清楚如何使其工作 那么如何禁用警
  • R 包文档:链接到整个包,而不是函数

    我想在我正在开发的一些函数的文档中引用另一个包 整个包 而不仅仅是其中的一个函数 我正在使用 Roxygen2 注释来记录我的包功能 我找不到使用 Roxygen2 创建整个第三方包的链接的方法 要链接到一个包函数 可以这样写 pkg fu
  • TDD:在重构代码的同时破坏所有现有的测试用例

    我已经开始在我的项目中遵循 TDD 但自从我开始 即使读了一些文章 我也很困惑 因为发展速度变慢了 每当我重构代码时 我都需要更改之前编写的现有测试用例 否则它们将开始失败 下面是我最近重构的一个类的例子 public class Sala