Man kan vel sagtens argumenterer for at, en test man har behov for at køre gentagende gange, ikke er en god test. Men der er bare situationer, hvor det giver god mening, uanset om det så er ‘korrekt’ eller ej. Test af et kompliceret regulært udtryk for eksempel. Det er nok også derfor at NUnit i de senere versioner, har fået en RowTest attribut, der gør det muligt at markerer en test metode som en: RowTest, og efterfølgende tilføje så mange Row-attributter som man lyster:
[RowTest]
[Row(7.95, 8)]
[Row(5.126, 5.25)]
[Row(10.37, 10.5)]
public void TestAfrunding(double price, string expected)
{
var sut = new Money(price);
Assert.AreEqual(expected, sut.Rounded);
}
Den feature i mbUnit, har jeg i øvrigt hørt flere udviklere fremføre, som argument for at skifte MStest ud. Det holder jo selvfølgelig ikke, men hvad gør man så egentlig, når man arbejder med MStest, og har behov for at gennemfører loopede tests?
Ja for det første, så er data dreven unittest ikke særligt godt dokumenteret på MSDN, og man har stort set kun et eksempel, men roder man lidt med Reflector, finder man hurtigt frem til det centrale. Vi har en attribut der hedder: DataSource, som vi kan dekorere vores testmetoder med. Infrastrukturen i MStest, hooker automatisk op på den datakilde, og udfører testen for hver datarow der måtte være i datakilden.
Man kan bruge alle datakilder: Sql, OleDb osv., og browser man lidt rundt i MStests private assemblies, finder man en XML datakilde, og så er der jo basis for et dejligt setup.
Jeg starter med at skrive min test:
private void TestAfrunding(decimal expected, decimal input)
{
var sut = new Money(input);
Assert.AreEqual(expected, sut.Rounded);
}
Læg mærke til at jeg ikke har dekoreret den med TestMethod-atributten. Jeg isolerer nemlig min test i denne metode, for at fjerne grim konverteringskode.
Så laver jeg en Xmlfil der indeholder den data jeg vil teste med. Her har man frie hænder, men mit eks. ser således ud:
<?xml version="1.0" encoding="utf-8" ?>
<data>
<row>
<price>7,95</price>
<output>8</output>
</row>
<row>
<price>7,75</price>
<output>7,75</output>
</row>
<row>
<price>7,22</price>
<output>7,25</output>
</row>
<row>
<price>7,12</price>
<output>7,00</output>
</row>
<row>
<price>7,13</price>
<output>7,25</output>
</row>
<row>
<price>7,125</price>
<output>7,25</output>
</row>
</data>
Selve testmetoden kommer til at se således ud:
[TestMethod,
DataSource("Microsoft.VisualStudio.TestTools.DataSource.XML",
"TestData.xml",
"Row",
DataAccessMethod.Sequential)]
public void RowTest()
{
// Xmldata konverteres
decimal expected = Convert.ToDecimal(TestContext.DataRow["output"]);
decimal input = Convert.ToDecimal(TestContext.DataRow["price"]);
// Her kaldes den egentlige test
TestAfrunding(expected, input);
}
Det fine ved dette framework, er at man i modsætning til mbUnit, kan rette i sine testdata, og køre testene, uden at skulle rekompilerer. Desuden har man separeret kode fra data, og det er jo altid godt.
Frameworket kører alle test selvom der er nogen der skulle fejle. Ser man efterfølgende på detaljerne i testresultatet, kan man direkte se hvilken datarow der fik testen til at fejle – nice;-)
Måske er denne metode lidt mere indviklet end i mbUnit, men når først man har prøvet at have hul igennem, virker det enormt intuitivt, og kan virkelig øge kvaliteten i de tests man laver.
Code on…