c# - TDD : Breaks all the existing test cases while refactoring the code

标签 c# tdd

我已经开始在我的项目中遵循 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。请帮助我理解。

最佳答案

Whenever I refactor my code, I need to change the existing test cases I have written before because they will start failing.

这肯定表明出现了问题。重构的流行定义类似于this

REFACTORING is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior.

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

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

在重构时引入新的协作者并没有错,但您应该以不破坏当前 API 契约的方式进行。

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

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

其中 SalaryCalculatorSalaryMessageFormatter 应该是产生与您最初拥有的相同可观察行为的实现。

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

关于c# - TDD : Breaks all the existing test cases while refactoring the code,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49350993/

相关文章:

PHPUnit - 我认为我不能使用模拟来测试依赖项注入(inject)容器是否正确?

triggers - Salesforce:检查 ApexTrigger 在测试方法中是否处于事件状态

visual-studio-2010 - 在 Visual Studio 2010 上调试 MS 测试时出现 "Downloading public symbols"?

ruby-on-rails - rails 4 : Factory Girl & Rspec with associated Model

c# - 在 C# 中使用 RTMP 或 RTSP 协议(protocol)

c# - 如何检测点击当前选定的 PivotItem 的标题

c# - 更新条件错误中的 SQL 数据不匹配

c# - RemoteAttribute 没有将查询字符串作为 IEnumerable<T> 正确传递

c# - 如何打开第二个表单?

unit-testing - 在 .NET Core 中的内存数据库与 Moqing 框架