2016-01-19 191 views
0

这里是TDD的新手。如果我以正确的方式接近,只需要一些指导。我正在寻找使用TDD将罗马数字字符串转换为整数。我有2个关注(还,如果任何人都可以指出的其他问题,请做):这是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

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

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); 

} 
+0

怎么样的算法?你认为还有改进的余地吗? – DrZeuso

+0

@DrZeuso:这是CodeReview站点的问题。但是我会将'Special'块移动到一个单独的非公开方法,然后使用'switch'语句而不是'if'语句来进行其他检查。 – jgauffin

1

重复永远不好(DRY)所以类似的测试应该用一次测试来表示。我不熟悉微软的单元测试框架,但在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); 
    } 
} 
+0

看起来在算法中也有一些重复。你认为还有改进的余地吗? – DrZeuso

相关问题