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

9

Sto leggendo The Art of Unit Testing di Roy Osherove. Sono nella sezione 7.2 Scrivere test mantenibili dove l'autore ha questa nota sull'odore del codice:

NOTE: When you refactor internal state to be visible to an outside test, could it be considered a code smell (a sign that something might be wrong in the code's design or logic)? It's not a code smell when you're refactoring to expose collaborators. It's a code smell if you're refactoring and there are no collaborators (so you don't need to stub or mock anything).

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:

A stub is a controllable replacement for an existing dependency (or collaborator) in the system.

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

    
posta programmer 20.08.2012 - 19:37
fonte

2 risposte

3

Penso che questo sia ciò che l'autore sta ottenendo.

Nel mio esempio di codice, ho una finestra temporale che accetta una quantità in uscita più un tempo di inizio e fine. L'intento è di disegnare una finestra di output su un intervallo di 24 ore. Una piega viene aggiunta quando l'ora di inizio è maggiore del tempo di arresto poiché si tratta di una finestra temporale che copre la mezzanotte.

È possibile scrivere test unitari che esercitano completamente l'oggetto senza esporre le variabili private. Quei bool privati e i tempi 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 dal momento che sono dei collaboratori.

L'esposizione del doppio output sarebbe un odore di codice poiché non è un collaboratore - è un elemento esplicitamente nascosto dalla classe stessa che ha una logica condizionale all'interno di GetOutput per determinare cosa deve essere restituito.

Scavare nei bool / timespans renderebbe i test unitari più completi. Dice che questo è buono.
Scavare nel doppio% di co_de richiederebbe una logica aggiuntiva nel tuo test unitario che riflettesse cosa stava facendo output . Questo sarebbe l'odore del codice a cui si riferisce.

public class TimeWindow
{
  private bool isConst;
  private bool spansMidnight;
  private TimeSpan start1;
  private TimeSpan stop1;
  private TimeSpan start2;
  private TimeSpan stop2;
  private double output;

  public TimeWindow( double out, TimeSpan start, TimeSpan stop)
  {
    output = out;

    if( start == stop )
      isConst = true;
    else if( start > stop )
    {
      spansMidnight = true;
      start1 = midnight;
      stop1 = stop;
      start2 = start;
      stop2 = midnight;
    }
    else 
    {
      start1 = start;
      stop1 = stop;
    }
  }

  public double GetOutput( TimeSpan time )
  {
    // some logic here on what / how to return
    ...
    return output;
  }

}
    
risposta data 20.08.2012 - 20:37
fonte
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 usa per esporre un metodo "Salva" a livello di istanza che gli oggetti che lavorano sulla classe di dominio possono chiamare per mantenere i cambiamenti apportati senza aver bisogno di conoscere il meccanismo (se questo è un progetto "buono" è una discussione per un altro giorno). Rifattorizzare la classe per esporre questo repository come argomento proprietà e / o costruttore, consentendo così il passaggio di un repository fittizio che possa 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 backing field e che gli accessors della proprietà provino che un nuovo input è valido in base a quello corrente (forse il nuovo valore non può mai essere inferiore a quello vecchio). È necessario testare questa convalida, ma si scopre che farlo richiede l'accesso al campo di supporto per impostare un valore iniziale che verrà quindi provato a sovrascrivere. Questa dovrebbe essere una bandiera rossa; se il test ha bisogno di accedere al campo di supporto (che è un dettaglio di implementazione; nessun consumatore dovrebbe mai deve sapere che è lì) al fine di ottenere l'oggetto in uno stato coerente per un test, in che modo il codice di produzione otterrebbe un oggetto coerente? Se esiste un metodo per fare in modo che il codice di produzione faccia la stessa cosa che fa il test, allora il test dovrebbe probabilmente imitare in questo modo. Se non c'è un modo valido per il codice di produzione per ottenere l'oggetto in questo stato, allora perché stai testando questo scenario?

    
risposta data 20.08.2012 - 20:39
fonte

Leggi altre domande sui tag