如何重构tdd中的测试?

时间:2015-06-01 07:51:41

标签: unit-testing tdd

我正在执行此TDD kata练习:http://osherove.com/tdd-kata-1

我制作了以下代码(在这个练习中从1到5分 - 我有单元测试):

public class StringCalculator
{
    private readonly string[] _defaultSeparators = { ",", "\n" };

    public int Add(string numbers)
    {
        // Parser section (string to list of ints)
        var separators = _defaultSeparators;

        var isSeparatorDefinitionSpecified = numbers.StartsWith("//");
        if (isSeparatorDefinitionSpecified)
        {
            var endOfSeparatorDefinition = numbers.IndexOf('\n');

            var separator = numbers.Substring(2, endOfSeparatorDefinition - 2);

            numbers = numbers.Substring(endOfSeparatorDefinition);
            separators = new[] { separator };
        }

        var numbersArray = numbers.Split(separators, StringSplitOptions.RemoveEmptyEntries);
        var numbersArrayAsInts = numbersArray.Select(int.Parse).ToArray();

        // Validator section
        var negativeNumbers = numbersArrayAsInts.Where(c => c < 0).ToArray();
        if (negativeNumbers.Any())
        {
            throw new Exception(string.Format("negatives not allowed ({0})", string.Join(", ", negativeNumbers)));
        }

        return numbersArrayAsInts.Sum();
    }
}

现在我想重构代码:

public int Add(string numbers)
{
    var numbersAsInts = CalculatorNumbersParser.Parse(numbers);

    CalculatorNumbersValidator.Validate(numbersAsInts);

    return numbersAsInts.Sum();
}

我应该如何规划重构以正确重构我的代码和单元测试?

我认为我应该将部分测试移动到新创建的实现类测试(CalculatorNumbersParserTests和CalculatorNumbersValidatorTests),更改一些现有测试并为Parse和Validate方法执行添加测试。

但是,在不破坏测试的情况下,这样做的正确方法是什么?

2 个答案:

答案 0 :(得分:4)

我会谨慎地反对移动测试,就好像你这样做,然后你的测试与实现有关,这意味着它们非常脆弱,因此每次你想要改变你的实现时你都必须改变你的测试。当您拥有庞大的代码库并且可能成为进行更改的禁止因素时,这很快就会变得昂贵。

您现有的测试应该指定字符串计算器的行为,因此只要您保持所需的行为,就可以将实现重构为任何内容。

我倾向于认为一个单位是一个行为单位&#39;并且可能需要几个课程来实现它。

如果你要将一些类放在不同的程序集中,事情可能会改变,此时你可能想在新程序集旁边进行一些新的测试,以确保这些组件的行为不会意外地改变,但是这种情况我怀疑你会这样做。

如果你开始在几个地方重用这些类,那么事情也可能会发生变化,此时你可能需要单独的测试来指定类的行为,而不考虑它们在这些地方的使用。

答案 1 :(得分:3)

我认为@Sam Holder涵盖了大多数事情。我要补充的一件事是,当你重新分解你的代码时,你应该标记你创建的任何类,你不会为internal编写特定的测试(我假设你正在使用.net),因此它们在包含它们的程序集之外是不可见的。

我倾向于认为public类应该自行测试,因为它们可以通过其他代码轻松地实例化你的程序集。而另一方面,internal类可以被视为实现细节,通常可以通过程序集公共接口进行测试。当然有一些例外情况,取决于您正在做什么/代码的复杂程度等,但这是我的一般规则。