2010-11-19 7 views
34

J'écris une application intensive en données. J'ai les tests suivants. Ils fonctionnent, mais ils sont assez redondants.Comment passer des objets dynamiques dans une fonction 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); 
} 

Les mêmes propriétés sont modifiées au début, tout comme les enfants des différents objets de conteneurs, et des valeurs de couple dans le changement d'assertion à la fin. J'ai besoin d'en écrire quelques dizaines, en vérifiant différentes propriétés. Je veux donc paramétrer le test. L'astuce consiste à passer l'objet conteneur en tant que paramètre au test. L'objet conteneur est instancié dans le set de test SetUp.

Ce que je veux obtenir ressemblerait à quelque chose comme ceci:

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

Mais cela ne fonctionne pas et je ne suis pas sûr de savoir comment faire fonctionner, ou s'il est possible.

Répondre

87

Je dépisté vers le bas. Je ne peux pas passer un objet instancié dans un test via TestCase car les attributs sont strictement pour les méta-données statiques. Mais l'équipe NUnit a une solution pour cela, TestCaseSource. Le message sur la liste NUnit qui a répondu à la question est here.

Voici ce que ma solution ressemble maintenant à:

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

pas aussi jolie que je l'espérais, pas aussi facile à lire. Mais il a réussi à réduire la duplication du code, ce qui devrait faciliter la maintenance et la résolution des problèmes.

+1

Je pense que la propriété CountEqualsZeroAndHouseGrossIsGreaterTestCases doit être statique – moyomeh

+6

Si vous utilisez C# 6+, au lieu d'utiliser le nom comme une chaîne, vous pouvez utiliser 'nameof'. [TestCaseSource (nameof (CountEqualsZeroAndHouseGrossIsGreaterTestCases))], ce qui le rend fortement typé. –

+2

À partir de NUnit 3, TestCaseSource est également limité aux sources statiques. – buckminst

0

Ne serait-il pas beaucoup plus facile d'avoir une méthode privée, une méthode de classe de base ou des classes auxiliaires qui le font pour vous?

Pour mes tests unitaires, j'ai besoin de nombreuses entités fictives car c'est une application très consommatrice de données. J'ai créé une structure de faux référentiels qui peuvent créer des entités initialisées à la volée, que je peux combiner pour constituer une structure de base de données représentative en mémoire.

Quelque chose comme ça pourrait fonctionner pour vous:

// 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

C'est une amélioration, mais il laisse encore énormément de redondance. Il est difficile de lire ce code et de comprendre rapidement ce qui est testé à chaque itération. Les paramètres indiqueraient clairement que la même chose est testée à différents niveaux. Tous ces tests s'effondreraient en un. –

+0

Il est déconseillé de réduire tous les tests en un seul test. Si vous avez des tests séparés pour des problèmes distincts, il est plus facile de savoir ce qui va bien et ce qui ne va pas. Si vous mettez trop dans un seul test, vous essayez trop à la fois et il devient plus difficile de résoudre les problèmes. http://www.infoq.com/presentations/integration-tests-scam a une bonne présentation qui parle aussi de ces questions. –

+3

Droite. Mais avec les tests paramétrés, j'écris le code une fois, mais il fonctionne comme si chaque ensemble de paramètres était un test séparé. Chaque TestCase obtient sa propre ligne dans le runner de test NUnit. Il est donc toujours clair quelle partie échoue exactement, mais la redondance de code est éliminée, ce qui économise du temps d'écriture et est plus facile à lire. –

4

Je passe des chaînes que je parser parfois, pense qu'il se lit assez bien, par exemple:

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