Come collaudi un'unità una funzione che cancella le proprietà?

3

Ho una funzione molto comune che ho sempre testato unitamente nello stesso modo, ma mi chiedo se c'è una soluzione migliore o se è anche possibile che sia coinvolto un odore di codice. Sembra un caso molto semplice ma ho una funzione che cancella le proprietà dell'oggetto. Lavorando in JavaScript, ecco un semplice esempio:

function Dog(name, owner) {
  this.name = name;
  this.owner = owner;

  this.reset = function() {
    this.name = '';
    this.owner = '';
  };
}

var puppy = new Dog('Max', 'Timmy');
console.log(puppy.name)  // logs "Max"
puppy.reset();
console.log(puppy.name)  // logs ""

Normalmente eseguo il test dell'unità impostando le proprietà, chiamando la funzione di cancellazione e quindi asserendo che le proprietà sono state effettivamente impostate sui valori predefiniti o "cancellate". Il motivo per cui sto chiedendo una soluzione così semplice è che, a causa del dogma, i test unitari dovrebbero avere solo 1 asserzione. Penso anche che una funzione di tipo "reset" potrebbe sfuggire di mano quando si tratta di un gran numero di proprietà (cioè un oggetto che è destinato a memorizzare lo stato di una SPA).

Sono certo che sto pensando troppo a questo, ma volevo ottenere qualche opinione / critica esterna per qualcosa che ho fatto per anni con lo stesso comportamento. Non riesco proprio a pensare a un modo migliore per farlo.

Un'altra domanda potrebbe essere, sono necessari test unitari che riguardano una funzione di ripristino? Per me sembrano quasi solo testare l'implementazione del linguaggio - simile a una proprietà getter / setter.

    
posta adam-beck 23.12.2014 - 15:16
fonte

5 risposte

3

I test unitari dovrebbero riguardare la logica e, in effetti, reset non contiene alcuna logica - non c'è ifs , no switch es, nessun loop in esso - in pratica, nessuna istruzione condizionale di qualsiasi tipo.

E sì, significa che testarlo si riduce a testare JavaScript come tale, come dici tu. Impostare a, b e c per svuotare le stringhe! A, b e c sono stati impostati per svuotare le stringhe? Buono. Buono JavaScript!

Quindi, dato che non esiste una logica , perché dovremmo avere qui la copertura del test unitario?

Suppongo che vorremmo averlo per proteggerci dallo scenario in cui stai aggiungendo un'altra proprietà alla classe, ma poi dimenticarti di reimpostarlo nella tua funzione reset .

Il problema qui è che dovresti anche aggiornare il tuo test dell'unità per rivelare questo bug, e se hai dimenticato di aggiornare la tua funzione reset , è ovvio che non avresti dovuto aggiornare anche testReset .

Oppure la tua piccola funzione speciale che restituisce tutti i contenuti del tuo singleton, ben confezionato per scopi di test.

Una possibile alternativa sarebbe quella di usare la riflessione (nel caso di JavaScript, è semplicemente un'iterazione delle proprietà ovviamente) per reimpostare tutte le proprietà esistenti, quindi solo l'unità testarla come utilità universale, anche su una classe stub arbitraria.

Naturalmente è probabile che tu abbia più problemi se vuoi preservare il valore di alcune proprietà piuttosto che cancellare tutto pulito.

Tutto sommato, è un compito difficile perché è un singleton che devi resettare. I singleton sono notoriamente cattivi per la testabilità.

Misko Hevery ha dedicato una serie di articoli e presentazioni a questo. Vedi:

risposta data 23.12.2014 - 17:12
fonte
5

Stai pensando troppo alla regola / linea guida "one assertion".

Il motivo della regola è che un test case dovrebbe testare solo un "comportamento" di una classe. Testando solo un comportamento per ogni caso di test, rendi molto più facile per te individuare dove è stato introdotto un errore quando un caso di test inizia a fallire.

Il problema è che è difficile dire quando viene violata la regola di base di testare un solo comportamento, anche perché le persone potrebbero non essere d'accordo su ciò che cade sotto "un solo comportamento".
Tuttavia, nella grande maggioranza dei casi, l'unico comportamento che si desidera verificare può essere verificato con una singola affermazione. Per questo motivo, la regola viene dichiarata come "si avrà solo una singola asserzione".
Per la minoranza dei casi in cui un'azione singola, indivisibile della classe ha più output e / o effetti collaterali (come il tuo metodo reset ), è perfettamente bene avere più affermazioni di asserzione. In effetti, stai ancora affermando solo una cosa, vale a dire che tutte le proprietà sono state cancellate.

    
risposta data 23.12.2014 - 16:01
fonte
2

Un approccio alternativo è costituito da due test, uno per ciascun componente della funzione di ripristino. O più, se ne hai di più. Questi sarebbero abbastanza semplici da creare.

Un punto chiave di test è sapere cosa sta fallendo quando qualcosa fallisce. Se esegui il test del metodo "reset" e fallisce, vuoi sapere che cosa non è riuscito. Sebbene non siano molto importanti nel tuo esempio, altre situazioni come questa potrebbero essere molto meno chiare su quale parte fallisce.

Testando ogni singola variabile, fintanto che il tuo flusso di lavoro sta scrivendo un test fallito quando aggiungi nuove variabili, ad esempio Breed al tuo cane, sarai costretto ad aggiungere Breed alla funzione di reset perché hai immediatamente un test fallito.

    
risposta data 23.12.2014 - 18:29
fonte
0

Probabilmente invece di decidere in base ad alcune autorità per visualizzare le migliori pratiche, è meglio ricordare perché stai scrivendo test unitari, ad esempio vuoi risparmiare tempo in seguito catturando bug in fase di sviluppo, prevenendo bug da raggiungere gli utenti e documentare il codice per gli sviluppatori (e possibilmente per gli altri).

Il principio decisivo, quindi, è se il tempo impiegato per scrivere i test sia giustificato nel raggiungimento di tali obiettivi. Naturalmente, questa è ancora un'ipotesi basata su informazioni limitate, ma è il meglio che si possa fare.

Quanto tempo ci vorrà per capire l'errore reale se scrivi un test rispetto ai test N? In che modo sarà più stabile di fronte alle mutevoli esigenze? In questo caso, personalmente, stimerei che scrivere un gran numero di test probabilmente non varrà la pena, e la semantica di "eliminare le proprietà" sembra implicare che dovrebbe eliminare anche le proprietà future. Si noti che questo potrebbe tecnicamente essere scritto in una singola asserzione comunque, ad esempio:

assert.isTrue(myLibrary.propertyCollection(objectUnderTest).All(function(x) {x==='';}));

Se ciò che ha senso nella tua situazione è qualcosa a cui puoi rispondere meglio di quanto posso.

    
risposta data 23.12.2014 - 19:12
fonte
-1

È possibile aggiungere una funzione che restituisce le proprietà nel loro stato predefinito racchiuso in un oggetto e poi un altro che restituisce il loro stato corrente. Confronta i valori di ritorno di ciascuna di queste funzioni e asserisci di conseguenza.

In questo particolare esempio, dovresti essere in grado di affermare true che puppy.reset().getNameAndOwner() deve essere uguale a puppy.getDefaultState() . (C'è un collegamento per il confronto degli oggetti in fondo al mio post. NON utilizzare == o === )

Vedi sotto

function Dog(name, owner) {
   this.name = name || '';
   this.owner = owner || '';

   this.reset = function() {
       this.name = '';
       this.owner = '';
   };

   this.getNameAndOwner = function() {
       var name = this.name
           owner = this.owner;
       return { name: name, owner: owner };
   }

   this.defaultState = function() {
       var emptyName = '',
           emptyOwner = '';
       return { name: emptyName, owner: emptyOwner };
   }
}

Come confrontare gli oggetti:

Confronto tra oggetti in JavaScript
Come determinare l'uguaglianza per due oggetti JavaScript?

    
risposta data 23.12.2014 - 15:25
fonte

Leggi altre domande sui tag