2010-11-19 11 views
34

Sto scrivendo un'app intensiva di dati. Ho i seguenti test. Funzionano, ma sono abbastanza ridondanti.Come passare oggetti dinamici in una funzione 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); 
} 

Le stesse proprietà sono modificate all'inizio, come i bambini di diversi oggetti del contenitore, e un valori di coppia nel cambiamento asserzione alla fine. Devo scriverne alcune decine, controllando proprietà diverse. Quindi voglio parametrizzare il test. Il trucco sta passando l'oggetto contenitore come parametro al test. L'oggetto contenitore viene istanziato nel set di verifica del dispositivo di prova.

Quello che voglio realizzare sarebbe simile a questa:

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

Ma questo non funziona e io non sono sicuro di come farlo funzionare, o se è possibile.

risposta

87

ho rintracciato giù. Non posso passare un oggetto istanziato in un test tramite TestCase perché gli attributi sono strettamente per meta-dati statici. Ma il team NUnit ha una soluzione per questo, TestCaseSource. Il post nell'elenco NUnit che ha risposto alla domanda è here.

Ecco quello che la mia soluzione ora assomiglia:

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

Non bella come speravo, non è così facile da leggere. Ma è riuscito a ridurre la duplicazione del codice, che dovrebbe rendere le cose più facili da mantenere e risolvere.

+1

Penso che la proprietà CountEqualsZeroAndHouseGrossIsGreaterTestCases dovrebbe essere statica – moyomeh

+6

Se si utilizza C# 6+, invece di utilizzare il nome come stringa è possibile utilizzare 'nameof'. [TestCaseSource (nameof (CountEqualsZeroAndHouseGrossIsGreaterTestCases))], che lo rende fortemente digitato. –

+2

A partire da NUnit 3, TestCaseSource è limitato alle origini statiche. – buckminst

0

Non sarebbe molto più semplice avere un metodo privato, un metodo di classe base o classi di supporto che fanno questo per te?

Per i miei test di unità, ho bisogno di molte molte entità fittizie perché si tratta di un'applicazione molto intensiva di dati. Ho creato una struttura di repository finti in grado di creare al volo entità inizializzate, che posso combinare per creare una struttura di database rappresentativa in memoria.

Qualcosa del genere potrebbe funzionare per voi:

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

Questo è un miglioramento, ma lascia ancora un sacco di ridondanza. È difficile leggere quel codice e ottenere rapidamente il succo di ciò che viene testato in ogni iterazione. I parametri chiarirebbero che la stessa cosa viene testata a diversi livelli. Tutti quei test collasserebbero in uno solo. –

+0

Non è consigliabile comprimere tutti i test in un singolo test. Se hai test separati per problemi separati, è più facile scoprire cosa va bene e cosa va storto. Se metti troppo in un singolo test, provi troppo in una volta e diventa più difficile risolvere i problemi. http://www.infoq.com/presentations/integration-tests-scam ha una buona presentazione che parla anche di questi problemi. –

+3

Giusto. Ma con i test parametrizzati, scrivo il codice una volta, ma funziona come se ogni set di parametri fosse un test separato. Ogni TestCase ottiene la propria linea nel corridore test NUnit. Quindi è ancora chiaro quale parte abbia esattamente esito negativo, ma la ridondanza del codice viene eliminata, il che consente di risparmiare tempo a scrivere ed è più facile da leggere. –

4

mi passano le stringhe che mi parse a volte, penso si legge abbastanza bene, ad esempio:

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