2010-11-19 15 views
34

Piszę aplikację intensywnie korzystającą z danych. Mam następujące testy. Działają, ale są dość zbędne.Jak przekazywać obiekty dynamiczne do funkcji 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); 
} 

Te same właściwości są modyfikowane na początku, podobnie jak elementy potomne różnych obiektów kontenera, a wartości para w zmianie asercji na końcu. Muszę napisać kilkadziesiąt z nich, sprawdzając różne właściwości. Więc chcę sparametryzować test. Sztuczka polega na przekazywaniu obiektu kontenera jako parametru do testu. Obiekt kontenera jest tworzony w urządzeniu testowym SetUp.

Co chcę osiągnąć będzie wyglądać mniej więcej tak:

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

Ale to nie działa i nie jestem pewien, jak to działało, lub jeśli jest to możliwe.

Odpowiedz

87

Śledziłem go. Nie mogę przekazać instancji obiektu do testu za pomocą TestCase, ponieważ atrybuty są wyłącznie dla statycznych metadanych. Ale zespół NUnit ma na to rozwiązanie, TestCaseSource. Wpis na liście NUnit, który odpowiedział na pytanie, to here.

Oto co moje rozwiązanie teraz wygląda:

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

Nie tak ładna, jak miałem nadzieję, nie tak łatwe do odczytania. Udało się jednak ograniczyć kopiowanie kodu, co powinno ułatwić konserwację i naprawę.

+1

Myślę, że właściwość CountEqualsZeroAndHouseGrossIsGreaterTestCases powinna być statyczna. – moyomeh

+6

Jeśli używasz C# 6+, zamiast używać nazwy jako łańcucha, możesz użyć "nameof". [TestCaseSource (nameof (CountEqualsZeroAndHouseGrossIsGreaterTestCases))], co czyni go silnie wpisanym. –

+2

Od NUnit 3, TestCaseSource jest również ograniczony do źródeł statycznych. – buckminst

0

Czy nie byłoby dużo łatwiej mieć prywatną metodę, klasę bazową lub klasy pomocnicze, które robią to za Ciebie?

Do moich testów jednostkowych potrzebuję wielu fałszywych jednostek, ponieważ jest to aplikacja bardzo intensywna dla danych. Stworzyłem strukturę fałszywych repozytoriów, które mogą tworzyć inicjalizowane obiekty w locie, które mogę łączyć w celu utworzenia reprezentatywnej struktury bazy danych w pamięci.

Coś takiego, który mógłby pracować dla Ciebie:

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

To poprawa, ale nadal pozostawia bardzo dużo nadmiarowości. Trudno jest odczytać ten kod i szybko dostać się do sedna tego, co jest testowane w każdej iteracji. Parametry dałyby jasno do zrozumienia, że ​​to samo jest testowane na różnych poziomach. Wszystkie te testy zapadną się w jedno. –

+0

Nie jest wskazane, aby zwinąć wszystkie testy w jeden test. Jeśli masz osobne testy dla osobnych zagadnień, łatwiej jest ustalić, co jest właściwe, a co nie. Jeśli umieścisz zbyt wiele w jednym teście, testujesz za dużo naraz i trudniej jest rozwiązać problemy. http://www.infoq.com/presentations/integration-tests-scam ma dobrą prezentację, która również mówi o takich problemach. –

+3

Dobrze. Ale przy sparametryzowanych testach piszę kod raz, ale działa tak, jakby każdy zestaw parametrów był osobnym testem. Każda TestCase pobiera własną linię w testerze NUnit. Więc nadal jest jasne, która część dokładnie się nie udaje, ale redundancja kodu jest wyeliminowana, co oszczędza czas pisania i jest łatwiejsze do odczytania. –

4

mijam ciągi że analizowania czasami, że to brzmi całkiem dobrze, na przykład:

[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()); 
} 
Powiązane problemy