简体   繁体   中英

How can I pass dynamic objects into an NUnit TestCase function?

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.

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.

I pass strings that I parse sometimes, and I think it reads pretty well.

Example:

[TestCase("15°", "-10°", 25, typeof(Degrees))]
[TestCase("-10°", "15°", -25, typeof(Degrees))]
[TestCase("-10°", "0°", -10, typeof(Degrees))]
[TestCase("-90°", "1.5707 rad", -3.1414, typeof(Radians))]
[TestCase("1.5707 rad", "-90°", 3.1414, typeof(Radians))]
[TestCase("1.5707 rad", "1.5707 rad", 0, typeof(Radians))]
public void SubtractionTest(string lvs, string rvs, double ev, Type et)
{
    var lv = Angle.Parse(lvs);
    var rv = Angle.Parse(rvs);
    var diff = lv - rv;
    Assert.AreEqual(ev, diff.Value, 1e-3);
    Assert.AreEqual(et, diff.Unit.GetType());
}

It would be much much easier to have a private method, base class method, or helper classes that do this for you.

For my unit tests, I need many many mock entities because it's a very data-intensive application. I've created a structure of mock repositories that can create initialized entities on the fly, which I can combine to build up a representative database structure in memory.

Something like that could work for you:

// Wild guess at the class name, but you get the idea
private void InitializeTotals(AggregateItem item)
{
    item.ItemCount = 0;
    item._volume = 0;
    item._houseGross = 1;
}

[Test]
public void DoSanityCheck_WithCountEqualsZeroAndHouseGrossIsGreater_InMerchantAggregateTotals_SetsWarning()
{
    InitializeTotals(report.Merchants[5461324658456716].AggregateTotals);

    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()
{
    InitializeTotals(report.AggregateTotals);

    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()
{
    InitializeTotals(report.AggregateTotals.LineItem["WirelessPerItem"]);

    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);
}
 [Test]
 [TestCase("textbox", true, "Text is empty", null, false)]
 [TestCase("textbox", false, "Text is empty", null, true)]
 public void Test_Component_Validation_and_ValidationText__Whether_IsMandatory_IsSet(string textbox, bool isMandatory, string validationText, string value, bool expectedValue)
 {
     // Arrange
     var mockPublicPortalService = new Mock<IPublicPortalService>();
     PublicAssessmentController controller = new PublicAssessmentController(mockPublicPortalService.Object);

     // Set Component properties
     var Component = new Component()
     {
         ComponentDatatype = textbox,
         IsMandatory = isMandatory,
         ValidationText = validationText,
         Value = value
     };

     var context = new ValidationContext(Component);

     // Act
     var results = new List<ValidationResult>();
     var isModelStateValid = Validator.TryValidateObject(Component, context, results, true);

     // Assert
     Assert.AreEqual(expectedValue, isModelStateValid);
     if (isModelStateValid == false)
     {
         Assert.IsTrue(results.Any(x => x.ErrorMessage == validationText));
     };
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM