这是TDD的正确方法吗?

时间:2016-01-19 07:01:38

标签: c# .net model-view-controller tdd

TDD新手。如果我以正确的方式接近这一点,只需要一些指导。我希望使用TDD将罗马数字字符串转换为整数。我有两个问题(如果有人可以指出其他问题,请做):

  • 感觉我可能正在编写太多类似的测试 I_returns_1,II_returns_2等。我应该将其中一些结合起来 测试?或者数到20,我真的应该进行20次测试吗?
  • 我还有足够的重构吗?否则,有什么建议吗?

    private static readonly Dictionary<string, int> Specials = new Dictionary<string, int>()
    {
        {"IV", 4},
        {"IX", 9}
    };
    
    public static int? Convert(string input)
    {
        if (input == null) return null;
    
        var processed = input;
        var counter = 0;
    
        foreach (var special in Specials)
        {
            if (!processed.Contains(special.Key)) continue;
    
            processed = processed.Replace(special.Key, "");
            counter = counter + special.Value;
        }
    
        for (int i = 0; i < processed.Length; i++)
        {
            if (processed[i] == 'X')
                counter = counter + 10;
            if (processed[i] == 'V')
                counter = counter + 5;
            if (processed[i] == 'I')
                counter++;
        }
    
        return counter;
    }
    

以上是上面创建的测试...

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void Check_Null_returns_Exception()
    {
        var result = Program.Convert(null);

        Assert.AreEqual(result,null);
    }
    [TestMethod]
    public void I_returns_1()
    {
        var result = Program.Convert("I");

        Assert.AreEqual(result, 1);
    }
    [TestMethod]
    public void II_returns_2()
    {
        var result = Program.Convert("II");

        Assert.AreEqual(result, 2);
    }

    [TestMethod]
    public void Multiple_I_returns_number()
    {
        var result = Program.Convert("III");

        Assert.AreEqual(result, 3);
    }

    [TestMethod]
    public void IV_returns_4()
    {
        var result = Program.Convert("IV");

        Assert.AreEqual(result, 4);
    }

    [TestMethod]
    public void V_returns_5()
    {
        var result = Program.Convert("V");

        Assert.AreEqual(result, 5);
    }

    [TestMethod]
    public void VI_returns_6()
    {
        var result = Program.Convert("VI");

        Assert.AreEqual(result, 6);
    }
    [TestMethod]
    public void VII_returns_7()
    {
        var result = Program.Convert("VII");

        Assert.AreEqual(7,result);
    }
    [TestMethod]
    public void VIII_returns_8()
    {
        var result = Program.Convert("VIII");

        Assert.AreEqual(8,result);
    }
    [TestMethod]
    public void IX_returns_9()
    {
        var result = Program.Convert("IX");

        Assert.AreEqual(9, result);
    }

    [TestMethod]
    public void X_returns_10()
    {
        var result = Program.Convert("X");

        Assert.AreEqual(10, result);
    }

    [TestMethod]
    public void Test_XI_to_XIII()
    {
        Assert.AreEqual(11, Program.Convert("XI"));
        Assert.AreEqual(12, Program.Convert("XII"));
        Assert.AreEqual(13, Program.Convert("XIII"));
    }
    [TestMethod]
    public void XIV_returns_14()
    {
        var result = Program.Convert("XIV");

        Assert.AreEqual(14, result);
    }
    [TestMethod]
    public void XV_returns_15()
    {
        var result = Program.Convert("XV");

        Assert.AreEqual(15, result);
    }

    [TestMethod]
    public void XVI_returns_16()
    {
        var result = Program.Convert("XVI");

        Assert.AreEqual(16, result);
    }
    [TestMethod]
    public void XVI_returns_17()
    {
        var result = Program.Convert("XVII");

        Assert.AreEqual(17, result);
    }
    [TestMethod]
    public void XVI_returns_18()
    {
        var result = Program.Convert("XVIII");

        Assert.AreEqual(18, result);
    }

    [TestMethod]
    public void XIX_returns_19()
    {
        var result = Program.Convert("XIX");

        Assert.AreEqual(19, result);
    }
    [TestMethod]
    public void XX_returns_20()
    {
        var result = Program.Convert("XX");

        Assert.AreEqual(20, result);
    }
}

2 个答案:

答案 0 :(得分:2)

首先,单元测试名称应该是有意义的,而不仅仅是描述代码。

II_returns_2并未解释测试试图保证的内容。 II_should_be_interpreted_as_the_roman_representation_of_2更好地解释了 业务规则

这很重要,因为当你回到代码一年后才知道读取测试名称II_returns_2,你就不会对你为什么编写该测试有任何疑问。 (好吧,在这个简单的例子中你可能会,但不是在典型的应用程序中)

接下来是方法名称将类似,但由于代码路径不同(应用程序根据罗马数执行不同的分支),您需要进行不同的测试。

幸运的是,一些不同的单元测试库支持使用属性来定义一组不同的测试值。

以下是xunit的示例。

[Theory]
[InlineData("I", 1)]
[InlineData("II", "2")]
[InlineData("III", "3")]
public void test(string roman, int number)
{
    var actual = Program.Convert(roman);

    Assert.AreEqual(actual, number);

}

答案 1 :(得分:1)

重复永远不会很好(DRY)所以类似的测试应该用单个测试来表示。我不熟悉Microsoft的单元测试框架,但在NUnit中,您可以使用TestCase属性并编写如下内容:

[TestFixture]
public class RomanNumeralConverter_Tests
{
   [TestCase("I", 1)]
   [TestCase("II", 2)]
   [TestCase("III", 3)]
   [TestCase("IV", 4)]
   [TestCase("V", 5)]
   [TestCase("VI", 6)]
   // etc...
   public void Convert_returns_decimal_representation(string roman, int expectedDecimal)
   {
      var result = Program.Convert(roman);
      Assert.AreEqual(expectedDecimal, result);
   }
}