Esiste un modo migliore per scrivere i test unitari di una serie di "AssertEquals"?

12

Ecco un esempio di base di ciò che deve essere il mio test unitario, usando qunit:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>

<link rel="stylesheet" href="qunit/qunit-1.13.0.css">
<script src = "qunit/qunit-1.13.0.js"></script>
<script src = "../js/fuzzQuery.js"></script>

<script>

test("Fuzz Query Basics", function()
        {
            equal(fuzzQuery("name:(John Smith)"), "name:(John~ Smith~)");
            equal(fuzzQuery("name:Jon~0.1"), "name:Jon~0.1");
            equal(fuzzQuery("Jon"), "Jon~");
            //etc

        }
    );

</script>
</head>
<body>
    <div id="qunit"></div>
</body>
</html>

Ora pensavo che questo fosse un po 'ripetitivo.

Potrebbe inserire tutti gli input / output in una matrice e farla scorrere tra loro.

test("Fuzz Query Basics", function()
        {
            var equals = [
                           ["name:(John Smith)", "name:(John~ Smith~)"],
                           ["name:Jon~0.1", "name:Jon~0.1"],
                           ["Jon", "Jon~"]
                           ];

            for (var i = 0; i<equals.length; i++)
                {
                    equal(fuzzQuery(equals[i][0]), equals[i][1]);               
                }

        }
    );

E questo funziona bene.

Il singolo vantaggio a cui posso pensare per questo secondo metodo è che se si scopre che in realtà non si desidera utilizzare equal è più facile apportare tale modifica in un unico punto.

In termini di leggibilità, non penso che sia conclusivo in entrambi i casi, anche se probabilmente preferisco il secondo.

Estrapolandolo ulteriormente, puoi inserire i casi di input / output in un file CSV separato, che potrebbe renderlo più facile da modificare.

La domanda è: quali sono le convenzioni generali sulla scrittura di questo tipo di test unitari?

C'è una ragione per cui non dovresti metterli negli array?

    
posta dwjohnston 22.01.2014 - 23:28
fonte

4 risposte

8

I test refactored hanno un odore: Logica test condizionale .

I motivi per evitare di scrivere la logica condizionale nei test sono duplici. La prima è che mina la tua capacità di essere sicuro che il tuo codice di test sia corretto, come descritto nell'articolo xUnit Patterns collegato.

Il secondo è che oscura il significato dei test. Scriviamo Metodi di test perché mettono la logica per testare un dato comportamento in un posto e ci permettono di dargli un descrittivo nome (vedi l'articolo originale di BDD di Dan North per un'esplorazione del valore dei buoni nomi per i test). Quando i tuoi test sono nascosti all'interno di una singola funzione con un ciclo for , oscura il significato del codice per il lettore. Non solo il lettore deve comprendere il ciclo, ma deve anche risolvere mentalmente tutti i diversi comportamenti testati all'interno del ciclo.

La soluzione, come sempre, è di alzare un livello di astrazione. Utilizza un framework di test che ti dia test parametrizzati , come xUnit.NET o Contesti do (dichiarazione di non responsabilità: ho scritto Contesti). Ciò consente di raggruppare i test di triangolazione per lo stesso comportamento insieme in modo naturale, mantenendo separati i test per i comportamenti separati.

    
risposta data 23.01.2014 - 01:13
fonte
5

Sembra che tu voglia veramente un test dell'unità Data Driven. Dato che hai menzionato l'uso di QUnit, ho trovato un plugin che abilita i test parametrizzati:

link

Non c'è nulla di ideologicamente sbagliato in un test guidato dai dati, purché il codice di test non sia condizionale. Guardando il tuo codice di test, sembra essere un ottimo candidato per un Test guidato dai dati.

Esempio di codice per il README GitHub:

QUnit
    .cases([
        { a : 2, b : 2, expectedSum : 4 },
        { a : 5, b : 5, expectedSum : 10 },
        { a : 40, b : 2, expectedSum : 42 }
    ])
    .test("Sum test", function(params) {
        var actualSum = sum(params.a, params.b);
        equal(actualSum, params.expectedSum);
    });
    
risposta data 06.02.2014 - 19:21
fonte
1

Ti stai ripetendo meno usando l'array che è più manutenibile. Un approccio che mi piace utilizzare è quello di avere un metodo separato che organizza, agisce e asserisce i test, ma accetta i parametri di input che sto testando, quindi ho 1 metodo di test per set di input.

Questo mi consente di dire immediatamente quali test / ingressi stanno fallendo.

    
risposta data 22.01.2014 - 23:33
fonte
0

Mi piace il tuo secondo approccio, ma vorrei aggiungere 2 punti

  • non utilizzare array per archiviare dati testati, poiché lavorare con gli indici non è un modo pulito
  • non utilizzare for loops

'

[
    {
        process: "name:(John Smith)",
        result: "name:(John~ Smith~)"
    },
    {
        process: "name:Jon~0.1", 
        result: "name:Jon~0.1"
    },
    {
        process: "Jon", 
        result: "Jon~"
    }
]
.forEach(function(data){

    var result = fuzzQuery(data.process);
    equal(result, data.result);
});

Non sono sicuro di qunit, ma un buon test runner ti mostrerà quale stringa di input ha avuto esito negativo e quale è stato il risultato previsto

    
risposta data 07.02.2014 - 01:06
fonte

Leggi altre domande sui tag