2013-05-12 67 views
3

我需要編寫一個解析器。有很多我想測試的輸入格式,輸出非常複雜。我應該如何用相似的測試來測試不同的輸入和輸出?C#單元測試解析器

例如

public class Person 
{ 
    public string Name; 
    public int Age; 
    public string Comment; 
} 

public interface IParser 
{ 
    Person Parse(string input); 
} 

public class Parser : IParser 
{ 
    public Person Parse(string input) 
    { 
     var fields = input.Split(','); 

     var p = new Person 
     { 
      Name = fields[0], 
      Age = int.Parse(fields[1]) 
     }; 

     p.Comment = fields.Length == 3 ? fields[2] : ""; 

     return p; 
    } 
} 

我寫這些測試...

public class ParserTests 
{ 
    protected string _input; 
    protected Person _expected; 

    private IParser _parser = new Parser(); 
    private Person _actual { get { return _parser.Parse(_input); } } 

    [TestMethod] 
    public void Parse_Name() 
    { 
     Assert.AreEqual(_expected.Name, _actual.Name); 
    } 

    [TestMethod] 
    public void Parse_Age() 
    { 
     Assert.AreEqual(_expected.Age, _actual.Age); 
    } 

    [TestMethod] 
    public void Parse_Comment() 
    { 
     Assert.AreEqual(_expected.Comment, _actual.Comment); 
    } 
} 

[TestClass] 
public class ParserTestsWithoutComment : ParserTests 
{ 
    public ParserTestsWithoutComment() 
    { 
     _input = "John,29"; 
     _expected = new Person { Name = "John", Age = 29, Comment = "" }; 
    } 
} 

[TestClass] 
public class ParserTestsWithComment : ParserTests 
{ 
    public ParserTestsWithComment() 
    { 
     _input = "Brian,99,test"; 
     _expected = new Person { Name = "Brian", Age = 99, Comment = "test" }; 
    } 
} 

我是新來的單元測試,我不知道如何下手更復雜的東西。我真正的輸入文件是比較複雜的滋味

PokerStars Hand #98451585362: Hold'em No Limit ($5/$10 USD) - 2013/05/12 9:25:04 CET [2013/05/12 3:25:04 ET] 
Table 'Soyuz-Apollo II' 6-max Seat #4 is the button 
Seat 1: Codrus426 ($1812.52 in chips) 
Seat 2: JMBigJoe ($2299.10 in chips) 
Seat 3: xinxin1 ($903.94 in chips) 
Seat 4: moshmachine ($1107 in chips) 
Seat 5: TopKat5757 ($1147 in chips) 
Seat 6: LukaschenkoA ($1274.96 in chips) 
TopKat5757: posts small blind $5 
LukaschenkoA: posts big blind $10 
*** HOLE CARDS *** 
Codrus426: calls $10 
JMBigJoe: raises $25 to $35 
xinxin1: folds 
moshmachine: folds 
TopKat5757: folds 
LukaschenkoA: folds 
Codrus426: calls $25 
*** FLOP *** [2h 3s 6h] 
Codrus426: checks 
JMBigJoe: bets $41 
Codrus426: calls $41 
*** TURN *** [2h 3s 6h] [2d] 
Codrus426: bets $40 
JMBigJoe: calls $40 
*** RIVER *** [2h 3s 6h 2d] [Qh] 
Codrus426: checks 
JMBigJoe: checks 
*** SHOW DOWN *** 
Codrus426: shows [9d Ah] (a pair of Deuces) 
JMBigJoe: mucks hand 
Codrus426 collected $244 from pot 
*** SUMMARY *** 
Total pot $247 | Rake $3 
Board [2h 3s 6h 2d Qh] 
Seat 1: Codrus426 showed [9d Ah] and won ($244) with a pair of Deuces 
Seat 2: JMBigJoe mucked 
Seat 3: xinxin1 folded before Flop (didn't bet) 
Seat 4: moshmachine (button) folded before Flop (didn't bet) 
Seat 5: TopKat5757 (small blind) folded before Flop 
Seat 6: LukaschenkoA (big blind) folded before Flop 

而且我希望它解析爲Hand類我的工作......

public class Hand 
{ 
    public long ID; 
    public string Stakes; 
    public DateTime Date; 

    public IDictionary<Street, decimal> Pots; 
    public decimal FinalPot; 
    public decimal Rake; 

    public Player Hero; 
    public IDictionary<Player, PlayerInfo> Players; 

    public IList<Card> Board; 

    public IList<Decision> Actions; 

    public Hand() 
    { 
     this.Players = new Dictionary<Player, PlayerInfo>(); 
     this.Board = new List<Card>(); 
     this.Actions = new List<Decision>(); 
     this.Pots = new Dictionary<Street, decimal>(); 
    } 
} 

public class PlayerInfo 
{ 
    public Player Player; 
    public decimal Stack; 
    public decimal Summary; 
    public Position Position; 
    public Card Holecards; 
} 
+2

如果您使用NUnit,你可以使用測試用例這個MSTEST,這是非常乾淨的海事組織 – 2013-05-12 06:30:36

+0

我同意 - 很乾淨。我無法同意@JohanLarsson的觀點,儘管NUnit現在主要用於單元測試,但NUnit會大幅改善任何事情。 MSTest也適合你在做什麼。 – J0e3gan 2013-05-12 06:35:13

回答

0

我覺得你的結構是相當不錯的,記住TMTOWTDI。

但是,確保明確測試Parse爲空,空(即零長度)和空白字符串。更復雜/預期路徑的情況當然是測試的關鍵,但這樣的簡單情況也很重要。

此外,您使用的是結構很可能被摺疊成ParserTests一個共同%MethodUnderTest%_With%Condition(s)%_Expect%ExpectedResult%測試用例命名約定和可選的Test Data Builder模式再拍類負責建立像你這樣的測試數據,現在正在做你的ParserTests

2

由於您將全局變量與繼承結合在一起,您的解決方案正在運行但難以理解。

如果您使用的是NUnit 2.5或更高版本,則可以使用Parameterized Tests with TestCaseAttribute

[TestCase("John,29","John",29,"")] 
[TestCase(",13","",13,"")] 
public void ParserTest(Sting stringToParse, String expextedName, int expectedAge, String expectedComment) 
{ 
    IParser _parser = new Parser(); 
    Person _actual = _parser.Parse(stringToParse); 

    Assert.AreEqual(expextedName, _actual.Name, stringToParse + " failed on Name"); 
    Assert.AreEqual(expextedAge, _actual.Age, stringToParse + " failed on Age"); 
    Assert.AreEqual(expextedComment, _actual.Comment, stringToParse + " failed on Comment"); 
} 

我覺得這樣更容易理解。

如果你需要和你在一起有模擬它的描述下在how-to-rowtest-with-mstest

+0

@Pankracy:k3b在參數化單元測試中對此進行了一些清理。這個想法讓我想起了現在的結構可以被摺疊成「ParserTests」。我已經完成了這個晚上,但會嘗試儘快跟上代碼,以更好地解釋我的意思。 – J0e3gan 2013-05-12 07:22:32

+0

對不起,我將更新我的第一篇文章,我想解析的真實數據。 – Pankracy1999 2013-05-12 07:27:54