Reputation: 107387
In NUnit
, is there any way to indicate that a Datapoint(s)Attribute
should be applied only to one theory only, if there is more than one theory in the same TestFixture
class?
Reason I ask is that I have usually followed a unit test convention where all methods of a test class (CUT) are tested by multiple [Test]
methods rolled into a
single test fixture class, and am now trying to move away from parameterized tests toward [Theory]
.
Or should I just continue to use the Values / Range / Random attributes of Parameterized tests for such tests?
e.g. Below, I want to ensure that different datapoints to the theories for add and divide:
// C.U.T.
public class BadMaths
{
public int BadAdd(int x, int y) { return x + y - 1; }
public int Divide(int x, int y) { return x / y; }
}
[TestFixture]
public class BadMathsTest
{
// Ideally I want 2 x different datapoints - one for Add, and a different one for divide
[Datapoints]
private Tuple<int, int>[] _points = new Tuple<int, int>[]
{
new Tuple<int, int>(20, 10),
new Tuple<int, int>(-10, 0),
};
[Theory]
public void AddTheory(Tuple<int, int> point)
{
Assume.That((long)point.Item1 + (long)point.Item2 < (long)int.MaxValue);
Assert.That(point.Item1 + point.Item2, Is.EqualTo(new BadMaths().BadAdd(point.Item1, point.Item2)));
}
[Theory]
public void DivideTheory(Tuple<int, int> point)
{
Assume.That(point.Item2 != 0); // Seems the best I can do - test is inconclusive
Assert.That(point.Item1 / point.Item2, Is.EqualTo(new BadMaths().Divide(point.Item1, point.Item2)));
}
}
Edit
The example given above isn't a good example of Theory
usage - it is a better fit to TestCaseSource
, and with the new Roslyn nameof
operator neither the [DataPoints]
nor [UsedImplicitly]
attributes are required on the source data.
[TestCaseSource(nameof(_points)]
public void EnsureAddPoints(Tuple<int, int> point)
{ ....
Upvotes: 2
Views: 1820
Reputation: 171
I don't believe there's any direct way of asking NUnit to use different datapoints of the same type for different theories. However there are two possible ways you can work around:
The first is to use different TextFixture classes for the tests that require different datapoint values:
[TestFixture]
public class BadMathsAdditionTest
{
// Ideally I want 2 x different datapoints - one for Add, and a different one for divide
[Datapoints]
private Tuple<int, int>[] _points = new Tuple<int, int>[]
{
new Tuple<int, int>(20, 10),
new Tuple<int, int>(-10, 0),
};
// add tests that use these datapoints
[Theory]
public void AddTheory(Tuple<int, int> point)
{
Assume.That((long)point.Item1 + (long)point.Item2 < (long)int.MaxValue);
Assert.That(point.Item1 + point.Item2, Is.EqualTo(new BadMaths().BadAdd(point.Item1, point.Item2)));
}
}
[TestFixture]
public class BadMathsDivisionTest
{
// Ideally I want 2 x different datapoints - one for Add, and a different one for divide
[Datapoints]
private Tuple<int, int>[] _points = new Tuple<int, int>[]
{
new Tuple<int, int>(20, 10),
};
// add test that use these datapoints
}
The second method requires a bit more work but arguably gives more readable code is to wrap each datapoint set in a different struct, like this:
// C.U.T.
public class BadMaths
{
public int BadAdd(int x, int y) { return x + y - 1; }
public int Divide(int x, int y) { return x / y; }
}
[TestFixture]
public class BadMathsTest
{
public struct AdditionData
{
public int First { get; set; }
public int Second { get; set; }
}
[Datapoints]
private AdditionData[] _points = new AdditionData[]
{
new AdditionData{First=20, Second=10},
new AdditionData{First=-10, Second=0}
};
public struct DivisionData
{
public int First { get; set; }
public int Second { get; set; }
}
[Datapoints]
private DivisionData[] _points2 = new DivisionData[]
{
new DivisionData{First=20, Second=10},
};
[Theory]
public void AddTheory(AdditionData point)
{
Assume.That((long)point.First + (long)point.Second < (long)int.MaxValue);
Assert.That(point.First + point.Second, Is.EqualTo(new BadMaths().BadAdd(point.First, point.Second)));
}
[Theory]
public void DivideTheory(DivisionData point)
{
Assume.That(point.Second != 0); // Actually you probably want to keep this condition anyway. Second==0 would be a separate test
Assert.That(point.First / point.Second, Is.EqualTo(new BadMaths().Divide(point.First, point.Second)));
}
}
Upvotes: 3