Test unitari: "È un odore di codice se stai eseguendo il refactoring e non ci sono collaboratori"?


9

Sto leggendo The Art of Unit Testing di Roy Osherove. Sono alla sezione 7.2 Scrivere test gestibili in cui l'autore ha questa nota sull'odore del codice:

NOTA: quando si refactoring lo stato interno per essere visibile a un test esterno, potrebbe essere considerato un odore di codice (un segno che qualcosa potrebbe essere sbagliato nella progettazione o nella logica del codice)? Non è un odore di codice quando si esegue il refactoring per esporre i collaboratori. È un odore di codice se stai eseguendo il refactoring e non ci sono collaboratori (quindi non devi stubare o deridere nulla).

EDIT : Ciò che l'autore intende per "collaboratori" sono le dipendenze. Alcuni dei suoi esempi di dipendenze sono classi che accedono a un database o che accedono al file system del sistema operativo. Qui è dove definisce lo stub e inizia a usare la parola collaboratore:

Uno stub è un sostituto controllabile per una dipendenza esistente (o collaboratore ) nel sistema.

L'autore non ha un esempio di questo odore di codice e non riesco a capire / immaginare come sarebbe. Qualcuno può spiegarlo un po 'di più e forse fornire un esempio concreto?


Penso che la confusione qui derivi dalla parola "Collaboratori". Devo ammettere che non sono sicuro di cosa significhi anche in questo contesto.
rossipedia,

@Bryan Ross, ho aggiornato il post con come l'autore usa la parola "collaboratore". Grazie!
programmatore

Risposte:


3

Penso che sia quello a cui sta arrivando l'autore.

Nel mio esempio di codice, ho una finestra di temporizzazione che accetta una quantità di output più un tempo di inizio e fine. L'intento è di disegnare una finestra di output in un periodo di 24 ore. Viene aggiunta una ruga quando l'ora di inizio è maggiore dell'ora di arresto perché si tratta di una finestra di temporizzazione che si estende a mezzanotte.

È possibile scrivere unit test che esercitano completamente l'oggetto senza esporre le variabili private. Quei bool privati ​​e fasce orarie sono i collaboratori a cui si riferisce quando espongono gli interni per i test unitari. Secondo il libro, esporre quegli interni NON sarebbe un odore di codice poiché sono collaboratori.

Esporre il doppio outputsarebbe un odore di codice poiché non è un collaboratore - è un elemento esplicitamente nascosto dalla classe stessa che ha una logica condizionale all'interno GetOutputper determinare cosa dovrebbe essere restituito.

Scavare nei bool / fasce orarie renderebbe i test unitari più completi. Dice che va bene.
Scavare nel doppio outputrichiederebbe una logica aggiuntiva nel test unitario che rispecchi ciò che GetOutputstava facendo. Questo sarebbe l'odore del codice a cui si riferisce.

classe pubblica TimeWindow
{
  bool privato isConst;
  mezzanotte private bool;
  private TimeSpan start1;
  TimeSpan privato stop1;
  timeSpan privato start2;
  TimeSpan privato stop2;
  doppia uscita privata;

  finestra temporale pubblica (doppia uscita, avvio TimeSpan, arresto TimeSpan)
  {
    output = out;

    if (start == stop)
      isConst = true;
    altrimenti if (start> stop)
    {
      spansMidnight = true;
      start1 = mezzanotte;
      stop1 = stop;
      start2 = inizio;
      stop2 = mezzanotte;
    }
    altro 
    {
      start1 = inizio;
      stop1 = stop;
    }
  }

  GetOutput pubblico doppio (tempo TimeSpan)
  {
    // qualche logica qui su cosa / come restituire
    ...
    uscita di ritorno;
  }

}

0

Diciamo che abbiamo una classe di dominio e questa classe di dominio ha una conoscenza diretta del livello di persistenza usando un repository, che utilizza per esporre un metodo "Salva" a livello di istanza che gli oggetti che lavorano sulla classe di dominio possono chiamare per persistere sui cambiamenti realizzato senza la necessità di conoscere il meccanismo (se si tratta di un progetto "buono" è una discussione per un altro giorno). Rifattorizzare la classe per esporre questo repository come argomento di proprietà e / o costruttore, consentendo così il passaggio di un repository beffardo in grado di garantire che venga effettuata la chiamata corretta, è in genere una buona cosa non solo per i test ma per la manutenibilità generale.

Ora, essendo una classe di dominio, ha dati di stato. Supponiamo per un momento che una delle proprietà stateful abbia un campo di supporto e gli accessor delle proprietà testino che un nuovo input è valido in base a quello corrente (forse il nuovo valore non può mai essere inferiore a quello precedente). Devi testare questa convalida, ma scopri che per farlo è necessario accedere al campo di supporto per impostare un valore iniziale che proverai a sovrascrivere. Questa dovrebbe essere una bandiera rossa; se il test necessita dell'accesso al campo di supporto (che è un dettaglio di implementazione, nessun consumatore dovrebbe maidevi sapere che è lì) al fine di riportare l'oggetto in uno stato coerente per un test, come farebbe il codice di produzione a ottenere un oggetto coerente? Se esiste un metodo per il codice di produzione per fare la stessa cosa del test, allora il test dovrebbe probabilmente imitare in quel modo. Se non esiste un modo valido per il codice di produzione per portare l'oggetto in questo stato, allora perché stai testando questo scenario?

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.