Domanda

Attualmente uso una semplice convenzione per i miei test unitari. Se ho una classe denominata " EmployeeReader " ;, creo una classe di test chiamata " EmployeeReader.Tests. Quindi creo tutti i test per la classe nella classe test con nomi come:

  • Reading_Valid_Employee_Data_Correctly_Generates_Employee_Object
  • Reading_Missing_Employee_Data_Throws_Invalid_Employee_ID_Exception

e così via.

Di recente ho letto di un diverso tipo di convenzione di denominazione utilizzato in BDD. Mi piace la leggibilità di questa denominazione, per finire con un elenco di test simile a:

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

e così via.

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

È stato utile?

Soluzione

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

Non è necessario accontentarsi dell'uno o dell'altro come standard. Usiamo una combinazione di entrambi, a seconda di quante "attività" sono diverse. dobbiamo testare per ogni classe.

Altri suggerimenti

La convenzione di denominazione che ho usato è:

  

functionName_shouldDoThis_whenThisIsTheSituation

Ad esempio, questi sarebbero alcuni nomi di test per la funzione 'pop' di uno stack

  

pop_shouldThrowEmptyStackException_whenTheStackIsEmpty

     

pop_shouldReturnTheObjectOnTheTopOfTheStack_whenThereIsAnObjectOnTheStack

Ritengo che il secondo sia migliore perché rende i test delle tue unità più leggibili agli altri poiché le righe lunghe rendono il codice più difficile da leggere o rendono più difficile scorrere. Se ritieni ancora che ci sia qualche ambiguità riguardo a ciò che fa il test, puoi aggiungere commenti per chiarire questo.

Parte del ragionamento alla base della seconda convenzione di denominazione a cui fai riferimento è che stai creando test e specifiche comportamentali allo stesso tempo. Stabilisci il contesto in cui stanno accadendo le cose e cosa dovrebbe effettivamente accadere in quel contesto. (Nella mia esperienza, le osservazioni / i metodi di prova spesso iniziano con " dovrebbe _, " quindi ottieni uno standard " When_the_invoicing_system_is_told_to_email_the_client, " " should_initiate_connection_to_mail_server " format.)

Ci sono strumenti che riflettono sui tuoi dispositivi di prova e producono un foglio delle specifiche html ben formattato, eliminando i caratteri di sottolineatura. Si finisce con una documentazione leggibile dall'uomo che è sincronizzata con il codice effettivo (purché si mantenga alta e accurata la copertura del test).

A seconda della storia / funzionalità / sottosistema su cui stai lavorando, queste specifiche possono essere mostrate e comprese dagli stakeholder non programmatori per la verifica e il feedback, che è al centro di Agile e BDD in particolare.

Uso il secondo metodo e mi aiuta molto a descrivere cosa dovrebbe fare il tuo software. Uso anche le classi nidificate per descrivere un contesto più dettagliato.

In sostanza, le classi di test sono contesti, che possono essere nidificati, e i metodi sono tutte 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à il seguente output nel mio test runner:

MyClassSpecification+When_foobar_is_42+GetAnswer
    should_return_42

Sono stato lungo le due strade che descrivi nella tua domanda, nonché alcune altre ... La tua prima alternativa è piuttosto semplice e facile da capire per la maggior parte delle persone. Personalmente mi piace di più lo stile BDD (il tuo secondo esempio) perché isola diversi contesti e raggruppa le osservazioni su quei contesti. L'unico aspetto negativo è che genera più codice, quindi iniziare a farlo sembra leggermente più ingombrante fino a quando non vedi i test accurati. Inoltre, se si utilizza l'ereditarietà per riutilizzare la configurazione dell'apparecchiatura, si desidera un testrunner che emetta la catena di ereditarietà. Prendi in considerazione una classe " An_empty_stack " e vuoi riutilizzarlo in modo da 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 lo supporta, i tuoi test conterranno informazioni ridondanti come: " When_five_is_pushed_on_empty_stack: An_empty_stack " solo per rendere piacevole l'output.

Voto per chiamare la classe del test case: EmployeeReaderTestCase e chiamare i metodi () come http: // xunitpatterns. com / Organization.html e http://xunitpatterns.com/Organization. html # test% 20Naming% 20Conventions

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top