Un’operazione si dice idempotente quando ritorna sempre lo stesso risultato anche eseguendola più volte con gli stessi input. Ad esempio, un’operazione di cancellazione di un dato potrebbe essere resa idempotente restituendo “OK” sia se è stato cancellato il dato, sia se il dato era già stato cancellato in precedenza o non è mai esistito.

Per testare le operazioni idempotenti, bisogna ri-eseguire il test (mi riferisco a test automatici) più volte con gli stessi input. Usare un ciclo all’interno del test potrebbe essere la prima soluzione che viene in mente, ma è anche la più sconveniente da usare, perché bisogna raccogliere manualmente gli errori durante le iterazioni per poi restituirli alla fine del test, e inoltre non si integra bene con i vari tool di visualizzazione dei risultati dei test (i.e. viene mostrato un unico “pallino” verde o rosso per il risultato complessivo del test, anziché un pallino per ogni iterazione + quello complessivo).

Diversi framework di testing offrono diverse soluzioni al problema. Vediamone alcune:

XUnit (JUnit, NUnit, ecc.)

Molti framework xUnit mettono a disposizione un’annotazione/attributo apposito per contrassegnare i test che devono essere ripetuti N volte. Ad esempio, in JUnit (Java) c’è l’annotazione @RepeatedTest(N), mentre in NUnit (C#) c’è l’attributo [Repeat(N)]. In entrambi i casi, nei risultati verrà riportato il risultato generale (ok se tutte le iterazioni sono ok, ko altrimenti) e i “sotto-risultati” per ogni iterazione.

MSTest V2

In MSTest V2 (C#) non c’è un attributo dedicato allo scopo, ma ci sono almeno due escamotage utilizzabili:

  • Creare un data driven test usando l’attributo [DataTestMethod] e un attributo [DataRow(1, 2, ..., N)] per ogni iterazione, dove il parametro è l’indice dell’iterazione (superfluo, ma può essere usato nel messaggio dell’asserzione);
  • Creare un nuovo attributo RepeatedTestMethodAttribute che estenda TestMethodAttribute aggiungendo una property IterN e facendo l’override del metodo Execute come mostrato di seguito. Si noti che il metodo è già predisposto per ritornare un array di risultati, che verranno mostrati nel test explorer sempre come sotto-risultati. Nell’esempio si mostra anche come personalizzare il DisplayName di ciascun risultato per contenere l’indice dell’iterazione corrispondente:
    public override TestResult[] Execute(
        ITestMethod testMethod)
    {
        var results = new TestResult[ExecNumber];

        for (var i = 0; i < IterN; i++)
        {
            results[i] = testMethod.Invoke(null);
            results[i].DisplayName =
            $"[{i+1} of {IterN}] {results[i].DisplayName}"; 
        };

        return results;
    }