Reputation: 66
I need to write a parser. There are many input formats which I want to test and the output is pretty complex. How should I test different inputs and outputs with similar tests?
For example
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;
}
}
I wrote these tests...
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" };
}
}
I'm new to unit testing and I'm not sure how to start with more complex stuff. My real input file is more complicated it's like
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
And I want parse it to a Hand
class I'm working on...
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;
}
Upvotes: 3
Views: 2607
Reputation: 14775
Your Solution is working but difficuilt to understand since you combine global variables with inheritance.
If you are using NUnit 2.5 or later you can use 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");
}
I think this is much easier to understand.
If you need to stay with mstest you have to simulated it as decribed in how-to-rowtest-with-mstest
Upvotes: 2
Reputation: 8938
I think the structure you have is pretty good, bearing in mind that TMTOWTDI.
Be sure, however, to explicitly test Parse
for null, empty (i.e. zero-length), and whitespace strings. More complicated/expected-path cases are key to test of course, but simple cases like this are important too.
Also, the structure you are using could very well be collapsed into ParserTests
with a common %MethodUnderTest%_With%Condition(s)%_Expect%ExpectedResult%
test-case naming convention and optionally the Test Data Builder pattern to make another class responsible for building test data like you are now doing in your ParserTests
subclasses
Upvotes: 0