2009-06-19 8 views
11

Attualmente utilizzo una semplice convenzione per i miei test di unità. Se ho una classe denominata "EmployeeReader", creo una classe di test denominato "EmployeeReader.Tests poi creo tutti i test per la classe nella classe di test con nomi come:.Devo cambiare la convenzione di denominazione per i miei test unitari?

  • Reading_Valid_Employee_Data_Correctly_Generates_Employee_Object
  • Reading_Missing_Employee_Data_Throws_Invalid_Employee_ID_Exception

e così via.

ho recentemente letto su un different type of naming convention utilizzato in BDD. mi piace la leggibilità di questa denominazione, t o finire con un elenco di test simile:

  • When_Reading_Valid_Employee (fixture)
    • Employee_Object_Is_Generated (metodo)
    • Employee_Has_Correct_ID (metodo)
  • When_Reading_Missing_Employee (fixture)
    • An_Invalid_Employee_ID_Exception_Is_Thrown (metodo)

e così via.

Qualcuno ha utilizzato entrambi gli stili di denominazione? Potete fornire consigli, vantaggi, svantaggi, trucchi, ecc. Per aiutarmi a decidere se passare o meno al mio prossimo progetto?

risposta

3

Il tuo secondo esempio (avere una fixture per ogni "task" logico, piuttosto che uno per ogni classe) ha il vantaggio di poter avere una logica di SetUp e TearDown diversa per ogni attività, semplificando così i singoli metodi di test e realizzandoli più leggibile.

Non è necessario accontentarsi dell'uno o dell'altro come standard. Usiamo una combinazione di entrambi, a seconda di quanti diversi "compiti" dobbiamo testare per ogni classe.

2

Ritengo che il secondo sia migliore perché rende i test dell'unità più leggibili per gli altri, poiché le linee lunghe rendono il codice più difficile da leggere o rendono più difficile sfogliare. Se ritieni che ci sia qualche ambiguità per ciò che fa il test, puoi aggiungere commenti per chiarirlo.

6

La convenzione di denominazione che ho usato è:

functionName_shouldDoThis_whenThisIsTheSituation

Ad esempio, questi sarebbero alcuni nomi di prova per la funzione di una pila 'pop'

pop_shouldThrowEmptyStackException_whenTheStackIsEmpty

pop _shouldReturnTheObjectOnTheTopOfTheStack_whenThereIsAnObjectOnTheStack

1

parte del ragionamento alla base della seconda convenzione di denominazione che si fa riferimento è che si sta creando i test e le specifiche comportamentali allo stesso tempo. Stabilisci il contesto in cui le cose stanno accadendo e cosa dovrebbe effettivamente accadere in quel contesto. (Nella mia esperienza, le osservazioni/metodi di prova iniziano spesso con "should_," in modo da ottenere uno standard "When_the_invoicing_system_is_told_to_email_the_client," formato "should_initiate_connection_to_mail_server".)

Ci sono strumenti che rifletteranno sui vostri dispositivi di test e l'output di un foglio delle specifiche HTML ben formattato, che elimina i caratteri di sottolineatura. Si finisce con la documentazione leggibile dall'uomo che è in sincronia con il codice reale (purché si mantenga alta e accurata la copertura del test).

A seconda della storia/funzione/sottosistema su cui si sta lavorando, queste specifiche possono essere mostrate e comprese dagli stakeholder non programmatori per la verifica e il feedback, che è al centro dell'agile e del BDD in particolare.

1

io uso secondo metodo, e aiuta davvero a descrivere ciò che il vostro software dovrebbe fare. Uso anche classi annidate per descrivere un contesto più dettagliato.

In sostanza, le classi di test sono contesti, che possono essere nidificati, e i metodi sono tutte le asserzioni di una riga. Ad esempio,

public class MyClassSpecification 
{ 
    protected MyClass instance = new MyClass(); 

    public class When_foobar_is_42 : MyClassSpecification 
    { 
     public When_foobar_is_42() { 
      this.instance.SetFoobar(42); 
     } 

     public class GetAnswer : When_foobar_is_42 
     { 
      private Int32 result; 

      public GetAnswer() { 
       this.result = this.GetAnswer(); 
      } 

      public void should_return_42() { 
       Assert.AreEqual(42, result); 
      } 
     } 
    } 
} 

che mi darà seguente output nel mio test runner:

MyClassSpecification+When_foobar_is_42+GetAnswer 
    should_return_42 
1

Sono stato giù per le due strade che descrivi nella tua domanda e pochi altri ... La tua prima alternativa è abbastanza semplice e facile da capire per la maggior parte delle persone. A me personalmente piace lo stile BDD (il tuo secondo esempio) in più perché isola diversi contesti e raggruppa le osservazioni su quei contesti. L'unico vero svantaggio è che genera più codice, quindi iniziare a farlo sembra leggermente più ingombrante fino a quando non si vedono i test accurati. Inoltre, se si utilizza l'ereditarietà per riutilizzare l'installazione di fixture, si desidera un testrunner che emetta la catena di ereditarietà. Considera una classe "An_empty_stack" e vuoi riutilizzarla per poi fare un'altra classe: "When_five_is_pushed_on: An_empty_stack" lo vuoi come output e non solo "When_five_is_pushed_on". Se il tuo testrunner non supporta questo tuo test conterrà informazioni ridondanti come: "When_five_is_pushed_on_empty_stack: An_empty_stack" solo per rendere l'output piacevole.