2010-11-19 43 views
34

我正在写一个数据密集型应用程序。我有以下测试。他们工作,但他们很冗余。如何将动态对象传递给NUnit TestCase函数?

[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); 
} 

相同的特性是在年初修改的,就像不同的容器对象的儿童,并在年底的说法变了几个值。我需要写几十个,检查不同的属性。所以我想参数化测试。诀窍是将容器对象作为参数传递给测试。容器对象在测试夹具SetUp中被实例化。

我想达成什么会是这个样子:

[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); 
} 

但是,这并不工作,我不知道如何使它工作,或者如果可能的话。

回答

87

我跟踪它。我无法通过TestCase将实例化对象传递到测试中,因为属性严格用于静态元数据。但NUnit团队为此提供了一个解决方案,即TestCaseSource。 NUnit列表中回答该问题的帖子是here

这里是我的解决方案现在的样子:我希望

public IEnumerable<TestCaseData> CountEqualsZeroAndHouseGrossIsGreaterTestCases 
{ 
    get 
    { 
     Setup(); 
     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); 
} 

不漂亮,不容易阅读。但是,它确实成功地减少了代码重复,这会使维护和修复更容易。

+1

我认为CountEqualsZeroAndHouseGrossIsGreaterTestCases属性应该是静态的 – moyomeh 2017-01-02 10:53:50

+6

如果您使用的是C#6+,而不是将名称用作字符串,则可以使用'nameof'。 [TestCaseSource(nameof(CountEqualsZeroAndHouseGrossIsGreaterTestCases))],这使得它强类型化。 – 2017-04-13 14:26:38

+2

从NUnit 3开始,TestCaseSource也仅限于静态资源。 – buckminst 2017-10-26 20:24:57

0

有没有为你做这个私有方法,基类方法或辅助类是不是很容易?

对于我的单元测试,我需要许多模拟实体,因为它是一个非常密集的应用程序。我创建了一个模拟存储库的结构,可以即时创建初始化实体,我可以将它们结合起来在内存中构建一个具有代表性的数据库结构。

类似的东西可能会为你工作:

// 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); 
} 
+0

这是一个进步,但它仍然留下一个可怕的很多冗余。很难阅读这些代码,并迅速了解每次迭代中测试的内容。参数可以清楚地表明同一件事正在不同层面上进行测试。所有这些测试将合并为一个。 – 2010-11-19 19:42:47

+0

将所有测试合并到单个测试中并不可取。如果您针对不同的问题分别进行测试,则更容易找出正确的和错误的。如果你在单个测试中投入过多,你会一次测试过多,解决问题变得更加困难。 http://www.infoq.com/presentations/integration-tests-scam有一个很好的介绍,也谈论这些问题。 – 2010-11-20 05:28:50

+3

对。但是对于参数化测试,我只写了一次代码,但它运行起来好像每一组参数都是单独的测试。每个TestCase在NUnit测试运行器中都有自己的行。因此,仍然清楚哪一部分完全失败,但是代码冗余被消除了,这节省了写入时间并且更易于阅读。 – 2010-11-22 15:46:52

4

我通过我有时解析字符串,认为它读得很好,例如:

[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()); 
}