I am writing a data-intensive application. I have the following tests. They work, but they're pretty redundant.
[Test] public void DoSanityCheck_WithCountEqualsZeroAndHouseGrossIsGreater_InMerchantAggregateTotals_SetsWarning() { report.Merchants[5461324658456716].AggregateTotals.ItemCount = 0; report.Merchants[5461324658456716].AggregateTotals._volume = 0; report.Merchants[5461324658456716].AggregateTotals._houseGross = 1; report.DoSanityCheck(); Assert.IsTrue(report.FishyFlag); Assert.That(report.DataWarnings.Where(x=> x is Reports.WarningObjects.ImbalancedVariables && x.mid == 5461324658456716 && x.lineitem == "AggregateTotals").Count() > 0); } [Test] public void DoSanityCheck_WithCountEqualsZeroAndHouseGrossIsGreater_InAggregateTotals_SetsWarning() { report.AggregateTotals.ItemCount = 0; report.AggregateTotals._volume = 0; report.AggregateTotals._houseGross = 1; report.DoSanityCheck(); Assert.IsTrue(report.FishyFlag); Assert.That(report.DataWarnings.Where(x=> x is Reports.WarningObjects.ImbalancedVariables && x.mid == null && x.lineitem == "AggregateTotals").Count() > 0); } [Test] public void DoSanityCheck_WithCountEqualsZeroAndHouseGrossIsGreater_InAggregateTotalsLineItem_SetsWarning() { report.AggregateTotals.LineItem["WirelessPerItem"].ItemCount = 0; report.AggregateTotals.LineItem["WirelessPerItem"]._volume = 0; report.AggregateTotals.LineItem["WirelessPerItem"]._houseGross = 1; report.DoSanityCheck(); Assert.IsTrue(report.FishyFlag); Assert.That(report.DataWarnings.Where(x=> x is Reports.WarningObjects.ImbalancedVariables && x.mid == null && x.lineitem == "WirelessPerItem").Count() > 0); }
The same properties are modified at the beginning, just as children of different container objects, and a couple of values in the assertion change at the end.
I need to write a few dozen of these, checking different properties. So I want to parameterize the test. The trick is passing the container object as a parameter to the test. The container object is instantiated in the test fixture SetUp.
I want to achieve something like this:
[TestCase(report.AggregateTotals.LineItem["WirelessPerItem"], 0, "WirelessPerItem")] [TestCase(report.AggregateTotals, 4268435971532164, "AggregateTotals")] [TestCase(report.Merchants[5461324658456716].AggregateTotals, 5461324658456716, "WirelessPerItem")] [TestCase(report.Merchants[4268435971532164].LineItem["EBTPerItem"], 4268435971532164, "EBTPerItem")] public void DoSanityCheck_WithCountEqualsZeroAndHouseGrossIsGreater_TestCase_SetsWarning(object container, long mid, string field) { container.ItemCount = 0; container._volume = 0; container._houseGross = 1; report.DoSanityCheck(); Assert.IsTrue(report.FishyFlag); Assert.That(report.DataWarnings.Where(x=> x is Reports.WarningObjects.ImbalancedVariables && x.mid == mid && x.lineitem == field).Count() > 0); }
But that doesn't work and I'm not sure how to make it work, or if it's possible.
All of the built-in attributes allow for data to be provided programmatically, but NUnit does not have any attributes that fetch the data from a file or other external source.
TestCase Attribute. The TestCase attribute in NUnit marks a method with parameters as a test method. It also provides the inline data that needs to be used when that particular method is invoked.
If you add a second parameter with the Values attribute, for per value of the first parameter, NUnit will add a test for every value of the second parameter. Another way to avoid having to write duplicate tests when testing the same behaviour with different inputs is to use TestCase attribute on the test itself.
I tracked it down. I can't pass an instantiated object into a test via TestCase because attributes are strictly for static meta-data. But the NUnit team has a solution for that, TestCaseSource. The post on the NUnit list that answered the question is here.
Here is what my solution now looks like:
public static IEnumerable<TestCaseData> CountEqualsZeroAndHouseGrossIsGreaterTestCases { get { yield return new TestCaseData(report, report.Merchants[4268435971532164].LineItem["EBTPerItem"], 4268435971532164, "EBTPerItem").SetName("ReportMerchantsLineItem"); yield return new TestCaseData(report, report.Merchants[5461324658456716].AggregateTotals, 5461324658456716, "WirelessPerItem").SetName("ReportMerchantsAggregateTotals"); yield return new TestCaseData(report, report.AggregateTotals, null, "AggregateTotals").SetName("ReportAggregateTotals"); yield return new TestCaseData(report, report.AggregateTotals.LineItem["WirelessPerItem"], null, "WirelessPerItem").SetName("ReportAggregateTotalsLineItem"); } } [TestCaseSource("CountEqualsZeroAndHouseGrossIsGreaterTestCases")] public void DoSanityCheck_WithCountEqualsZeroAndHouseGrossIsGreater_TestCase_SetsWarning(Reports.ResidualsReport report, Reports.LineItemObject container, long? mid, string field) { container.ItemCount = 0; container._volume = 0; container._houseGross = 1; report.DoSanityCheck(); Assert.IsTrue(report.FishyFlag); Assert.That(report.DataWarnings.Where(x=> x is Reports.WarningObjects.ImbalancedVariables && x.mid == mid && x.lineitem == field).Count() > 0); }
It is not as pretty as I hoped and is not as easy to read. But it did succeed on cutting down code duplication, which should make things easier to maintain and fix.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With