C# – Usa l'attributo DynamicData per passare funzioni e oggetti in test parametrizzati

C# – Usa l'attributo DynamicData per passare funzioni e oggetti in test parametrizzati

Lo scopo dei test parametrizzati è eliminare i test duplicati. Esistono due modi per passare i parametri in un test parametrizzato:l'attributo DataRow e l'attributo DynamicData.

Con DataRow, il problema è che puoi passare solo costanti e array. Non puoi passare i tipi di riferimento. Quando si tenta di passare i tipi di riferimento, viene visualizzato il seguente errore in fase di compilazione:

È qui che entra in gioco l'attributo DynamicData. Specificare un metodo (o proprietà) del generatore di dati di test. Questo metodo generatore restituisce un elenco di array di parametri di test. Ogni bucket nell'elenco è un'esecuzione di test diversa.

Di seguito viene illustrato come aggiungere l'attributo DynamicData a uno unit test, puntando a un metodo di test statico chiamato GetTestData :

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

Ed ecco il GetTestData metodo del generatore di dati di prova:

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)

Ogni oggetto[] è un'esecuzione di test diversa. In questo esempio, i parametri decimali sono un esempio di passaggio dei tipi di riferimento. E il parametro Func è un esempio di passaggio di una funzione al test parametrizzato.

Quando eseguo il test, ottengo i seguenti risultati del test. Come puoi vedere, ha eseguito il test con tre set di parametri.

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 ha molti odori di prova:utilizzali a tua discrezione

Potresti aver esaminato l'esempio DynamicData sopra e il tuo "allarme di odore di codice" ha iniziato a suonare, e per una buona ragione. Il suo utilizzo provoca molti odore di prova . L'utilizzo di DynamicData è una scelta pragmatica. È un compromesso tra avere test duplicati e avere odori di test. Può avere senso nella tua data situazione.

Di seguito elencherò alcuni degli odori di prova.

  • Prova l'odore n. 1 – Quando uno dei test case fallisce, ottieni informazioni inutili su quale test non è riuscito.

Ad esempio, supponiamo che il test contro Calculator.Multiply() non sia riuscito. Questo produce i seguenti risultati del test:

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)

Puoi facilmente dire quale test case ha fallito? Non proprio. Puoi dirlo solo andando e guardando il metodo del generatore di dati di test e confrontando alcuni dei parametri con il test case.

Se invece avessi unit test separati, direbbe esplicitamente che il test case Moltiplica non è riuscito.

  • Prova l'odore n. 2 – La fase di arrangiamento viene eseguita al di fuori del test. Idealmente, i passaggi di organizzazione-atto-affermazione sarebbero tutti contenuti nel test, rendendolo più facile da capire.

Nota:questo è lo stesso motivo per cui l'attributo ExpectedException era un odore di prova e perché hanno introdotto Assert.ThrowsException in MSTestv2.

  • Prova l'odore n. 3 – DynamicData porta a codice eccessivamente complicato.

DynamicData è difficile da capire:è indiretto e complesso. Si passa in nome di un metodo generatore di dati di prova (indiretto). Questo restituisce un elenco di oggetti [] (indiretti). Ogni oggetto[] è un test case diverso che contiene parametri diversi. Solo guardando l'approccio DynamicData, non è intuitivo. Inoltre, l'oggetto[] rimuove contemporaneamente la sicurezza dei parametri e del tipo. Il compilatore non può imporre il passaggio di un oggetto[] con il numero corretto di parametri o parametri con i tipi appropriati.

Questo controlla tutte le caselle per codice eccessivamente complicato che è davvero difficile da capire e mantenere. Tuttavia, dovrai decidere se vale la pena questa eccessiva complessità per eliminare i test duplicati.