C# – Verwenden Sie das DynamicData-Attribut, um Funktionen und Objekte an parametrisierte Tests zu übergeben

C# – Verwenden Sie das DynamicData-Attribut, um Funktionen und Objekte an parametrisierte Tests zu übergeben

Der Zweck parametrisierter Tests besteht darin, doppelte Tests zu eliminieren. Es gibt zwei Möglichkeiten, Parameter an einen parametrisierten Test zu übergeben:das DataRow-Attribut und das DynamicData-Attribut.

Bei DataRow besteht das Problem darin, dass Sie nur Konstanten und Arrays übergeben können. Referenztypen können nicht übergeben werden. Wenn Sie versuchen, Referenztypen zu übergeben, erhalten Sie den folgenden Kompilierungsfehler:

Hier kommt das DynamicData-Attribut ins Spiel. Sie geben eine Testdatengeneratormethode (oder -eigenschaft) an. Diese Generatormethode gibt eine Liste von Testparameter-Arrays zurück. Jeder Bucket in der Liste ist ein anderer Testlauf.

Im Folgenden wird gezeigt, wie das DynamicData-Attribut zu einem Komponententest hinzugefügt wird, das auf eine statische Testmethode namens GetTestData verweist :

[DynamicData(nameof(GetTestData), DynamicDataSourceType.Method)] //arrange
[TestMethod()]
public void TestMathOps(decimal a, decimal b, Func<decimal, decimal, decimal> calculatorOperation, decimal expectedValue)
{
	//act
	var actual = calculatorOperation(a, b);

	//assert
	Assert.AreEqual(expectedValue, actual);
}
Code language: C# (cs)

Und hier ist das GetTestData Testdatengenerator-Methode:

private static IEnumerable<object[]> GetTestData() 
{
	return new List<object[]>()
	{
		new object[]{ 1.2m, 2.3m, (Func<decimal, decimal, decimal>)Calculator.Add, 3.5m },
		new object[]{ 1.5m, 0.5m, (Func<decimal, decimal, decimal>)Calculator.Subtract, 1.0m },
		new object[]{ 1.5m, 2.0m, (Func<decimal, decimal, decimal>)Calculator.Multiply, 3.0m }
	};
}
Code language: PHP (php)

Jedes Objekt[] ist ein anderer Testlauf. In diesem Beispiel sind die dezimalen Parameter ein Beispiel für die Übergabe von Referenztypen. Und der Parameter Func ist ein Beispiel für die Übergabe einer Funktion an den parametrisierten Test.

Wenn ich den Test durchführe, erhalte ich die folgenden Testergebnisse. Wie Sie sehen können, wurde der Test mit drei Parametersätzen ausgeführt.

Test has multiple result outcomes
   4 Passed

Results

    1)  TestMathOps
      Duration: 12 ms

    2)  TestMathOps (1.2,2.3,System.Func`3[System.Decimal,System.Decimal,System.Decimal],3.5)
      Duration: 4 ms

    3)  TestMathOps (1.5,0.5,System.Func`3[System.Decimal,System.Decimal,System.Decimal],1.0)
      Duration: < 1 ms

    4)  TestMathOps (1.5,2.0,System.Func`3[System.Decimal,System.Decimal,System.Decimal],3.0)
      Duration: < 1 msCode language: plaintext (plaintext)

DynamicData hat viele Testgerüche – verwenden Sie nach eigenem Ermessen

Sie haben sich vielleicht das obige DynamicData-Beispiel angesehen und Ihr „Code-Geruchsalarm“ hat angefangen, loszugehen, und das aus gutem Grund. Die Verwendung führt zu vielen Testgerüchen . Die Verwendung von DynamicData ist eine pragmatische Entscheidung. Es ist ein Kompromiss zwischen doppelten Tests und Testgerüchen. Es kann in Ihrer Situation sinnvoll sein.

Ich werde unten ein paar der Testgerüche auflisten.

  • Testgeruch Nr. 1 – Wenn einer der Testfälle fehlschlägt, erhalten Sie nutzlose Informationen darüber, welcher Test fehlgeschlagen ist.

Nehmen wir zum Beispiel an, der Test gegen Calculator.Multiply() ist fehlgeschlagen. Dies erzeugt die folgenden Testergebnisse:

TestMathOps (1.5,2.0,System.Func`3[System.Decimal,System.Decimal,System.Decimal],3.0)
      Duration: 21 ms

      Message: 
        Assert.AreEqual failed. Expected:<3.0>. Actual:<-0.5>. Code language: plaintext (plaintext)

Können Sie leicht erkennen, welcher Testfall fehlgeschlagen ist? Nicht wirklich. Sie können dies nur feststellen, indem Sie sich die Testdatengeneratormethode ansehen und einige der Parameter mit dem Testfall abgleichen.

Wenn Sie stattdessen separate Komponententests hätten, würde es explizit sagen, dass der Multiply-Testfall fehlgeschlagen ist.

  • Testgeruch Nr. 2 – Der Anordnungsschritt wird außerhalb des Tests durchgeführt. Idealerweise wären die Schritte Arrange-Act-Assert alle im Test enthalten, was das Verständnis erleichtert.

Hinweis:Dies ist derselbe Grund, warum das Attribut „ExpectedException“ ein Testgeruch war und warum Assert.ThrowsException in MSTestv2 eingeführt wurde.

  • Testgeruch Nr. 3 – DynamicData führt zu übermäßig kompliziertem Code.

DynamicData ist schwer zu verstehen – es ist indirekt und komplex. Sie übergeben den Namen einer Testdatengeneratormethode (indirekt). Dies gibt eine Liste von Objekten [] zurück (indirekt). Jedes Objekt[] ist ein anderer Testfall, der unterschiedliche Parameter enthält. Wenn man sich nur den DynamicData-Ansatz ansieht, ist er nicht intuitiv. Darüber hinaus entfernt das Objekt [] gleichzeitig die Parametersicherheit und die Typsicherheit. Der Compiler kann unmöglich die Übergabe eines Objekts [] mit der richtigen Anzahl von Parametern oder Parametern mit den richtigen Typen erzwingen.

Dadurch werden alle Kästchen auf übermäßig komplizierten Code überprüft, der wirklich schwer zu verstehen und zu warten ist. Sie müssen jedoch entscheiden, ob sich diese übermäßige Komplexität lohnt, um doppelte Tests loszuwerden.