2010-11-19 18 views
34

Estoy escribiendo una aplicación de datos intensivos. Tengo las siguientes pruebas. Ellos trabajan, pero son bastante redundantes.¿Cómo pasar objetos dinámicos a una función 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); 
} 

Las mismas propiedades se modifican al comienzo, al igual que los niños de diferentes objetos de contenedor, y un par de valores en el cambio afirmación al final. Necesito escribir algunas docenas de estos, verificando diferentes propiedades. Entonces quiero parametrizar la prueba. El truco es pasar el objeto contenedor como un parámetro para la prueba. El objeto contenedor se crea una instancia en SetUp del dispositivo de prueba.

Lo que yo quiero lograr sería algo como esto:

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

Pero eso no funciona y no estoy seguro de cómo hacer que funcione, o si es posible.

Respuesta

87

que lo rastreó. No puedo pasar un objeto instanciado a una prueba a través de TestCase porque los atributos son estrictamente para metadatos estáticos. Pero el equipo de NUnit tiene una solución para eso, TestCaseSource. La publicación en la lista NUnit que respondió a la pregunta es here.

Esto es lo que mi solución ahora se ve así:

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

No es tan bonita como esperaba, no es tan fácil de leer. Pero sí logró reducir la duplicación de código, lo que debería facilitar el mantenimiento y la reparación.

+1

Creo que la propiedad CountEqualsZeroAndHouseGrossIsGreaterTestCases debe ser estática – moyomeh

+6

Si está utilizando C# 6+, en lugar de usar el nombre como una cadena, puede usar 'nameof'. [TestCaseSource (nameof (CountEqualsZeroAndHouseGrossIsGreaterTestCases))], lo que lo hace fuertemente tipado. –

+2

A partir de NUnit 3, TestCaseSource también está limitado a fuentes estáticas. – buckminst

0

¿No sería mucho más fácil tener un método privado, un método de clase base o clases de ayuda que lo hagan por usted?

Para mis pruebas unitarias, necesito muchas entidades simuladas porque es una aplicación muy intensiva de datos. Creé una estructura de repositorios simulados que pueden crear entidades inicializadas sobre la marcha, que puedo combinar para construir una estructura de base de datos representativa en la memoria.

Algo así podría funcionar para usted:

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

Eso es una mejora, pero todavía deja una gran cantidad de redundancia. Es difícil leer ese código y obtener rápidamente la esencia de lo que se prueba en cada iteración. Los parámetros dejarían en claro que lo mismo se está probando en diferentes niveles. Todas esas pruebas colapsarían en una sola. –

+0

No es recomendable colapsar todas las pruebas en una sola prueba. Si tiene pruebas separadas para problemas separados, es más fácil averiguar qué va bien y qué va mal. Si pone demasiado en una sola prueba, prueba demasiado a la vez y se vuelve más difícil resolver los problemas. http://www.infoq.com/presentations/integration-tests-scam tiene una buena presentación que también habla sobre estos temas. –

+3

Derecha. Pero con las pruebas parametrizadas, escribo el código una vez, pero se ejecuta como si cada conjunto de parámetros fuera una prueba separada. Cada TestCase tiene su propia línea en el corredor de prueba NUnit. Por lo tanto, todavía está claro qué parte está fallando exactamente, pero se elimina la redundancia de código, lo que ahorra tiempo de escritura y es más fácil de leer. –

4

que pasar cadenas que PARSE veces, piensan que lee bastante bien, ejemplo:

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