I have created an NUnit Theory to help me test some code. The actual code being tested isn't as important to this question as the data that I'm using to test it. Namely, the hours and minutes of time in a 24-hour clock.
I wrote my fixture this way in order to capitalize on the features and conform to the limitations within the NUnit 2.6 Theory feature. In particular, I felt that I had to create classes such as Hour and Minute in order to work-around the feature that Datapoints are matched to arguments by exact type.
[TestFixture]
public class TimeWindowParserTheoryFixture
{
public class Hour
{
public int Value;
}
public class Minute
{
public int Value;
public string AsString { get { return Value.ToString("00"); } }
}
[Datapoints]
public IEnumerable<Hour> Hours
{
get
{
return Enumerable
.Range(0, 25)
.Select(v => new Hour() { Value = v })
.Union(Enumerable.Repeat((Hour)null, 1));
}
}
[Datapoints]
public IEnumerable<Minute> Minutes
{
get
{
return Enumerable
.Range(0, 60)
.Select(v => new Minute() { Value = v })
.Union(Enumerable.Repeat((Minute)null, 1));
}
}
[Datapoints]
public IEnumerable<string> Separators
{
get { return new[] { " ", "-" }; }
}
[Theory]
public void ValidHours(Hour startHour,
Minute startMinute,
Hour endHour,
Minute endMinute,
string separator)
{
Assume.That(startHour != null);
Assume.That(endHour != null);
var parser = new TimeWindowParser();
var startMinutesString = String.Format("{0}{1}", startMinute == null ? "" : ":", startMinute == null ? "" : startMinute.AsString);
var endMinutesString = String.Format("{0}{1}", endMinute == null ? "" : ":", endMinute == null ? "" : endMinute.AsString);
var pattern = String.Format("{0}{1}{2}{3}{4}{5}{6}", startHour, startMinutesString, "", separator, endHour, endMinutesString, "");
//Console.WriteLine(pattern);
var result = parser.Parse(pattern);
Assert.That(result, Is.Not.Null);
Assert.That(result.Start, Is.EqualTo(startHour));
Assert.That(result.End, Is.EqualTo(endHour));
}
}
What I found is that the size of the data set produced during the default combinatorial logic of NUnit results in a set so large that I run out of memory. It doesn't seem like the way I've set up my test and data should be a problem but since it obviously is so I'm asking for advice on how to think about this problem differently. Here is the OutOfMemoryException stack trace that I get.
System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
at System.Text.StringBuilder.ExpandByABlock(Int32 minBlockCharCount)
at System.Text.StringBuilder.Append(Char* value, Int32 valueCount)
at System.Text.StringBuilder.AppendHelper(String value)
at System.Text.StringBuilder.Append(String value)
at NUnit.Core.MethodHelper.GetDisplayName(MethodInfo method, Object[] arglist)
at NUnit.Core.Builders.NUnitTestCaseBuilder.BuildSingleTestMethod(MethodInfo method, Test parentSuite, ParameterSet parms)
This exception is odd in itself in that it appears to be generated by simply trying to get the Test method's name (see GetDisplayName). I'm not sure if this is a bug (known or otherwise). BTW, I get a very similar OOM exception when I re-wrote this fixture using the less-experimental Range and Value attributes used in Parameterized tests.