File singoli o multipli per unità che testano una singola classe?

19

Nella ricerca delle best practice di test delle unità per aiutare a mettere insieme le linee guida per la mia organizzazione, mi sono imbattuto nella questione se sia meglio o utile separare i test di prova (classi di test) o mantenere tutti i test per una singola classe in un file.

Fwiw, mi riferisco ai "unit test" nel senso che sono test white-box rivolti a una singola classe, un'asserzione per test, tutte le dipendenze derise, ecc.

Uno scenario di esempio è una classe (chiamala documento) che ha due metodi: CheckIn e Checkout. Ogni metodo implementa varie regole, ecc. Che controllano il loro comportamento. Seguendo la regola one-assertion-per-test, avrò più test per ciascun metodo. Posso posizionare tutti i test in una singola classe DocumentTests con nomi come CheckInShouldThrowExceptionWhenUserIsUnauthorized e CheckOutShouldThrowExceptionWhenUserIsUnauthorized .

Oppure potrei avere due classi di test separate: CheckInShould e CheckOutShould . In questo caso, i miei nomi di test verrebbero abbreviati ma sarebbero organizzati in modo che tutti i test per un comportamento specifico (metodo) siano insieme.

Sono sicuro che ci sono pro e contro in entrambi i casi e mi sto chiedendo se qualcuno ha seguito il percorso con più file e, in tal caso, perché? Oppure, se hai optato per l'approccio a singolo file, perché ritieni che sia meglio?

    
posta SonOfPirate 13.12.2011 - 15:50
fonte

8 risposte

1

Vorrei poter ricordare / trovare il link in cui è stata dimostrata la tecnica che ho scelto di adottare. In sostanza creo una singola classe astratta per ogni classe sottoposta a test che contiene dispositivi di test annidati (classi) per ciascun membro sottoposto a test. Ciò fornisce la separazione inizialmente desiderata ma mantiene tutti i test nello stesso file per ogni posizione. Inoltre, questo genera un nome di classe che consente un facile raggruppamento e ordinamento nel test runner.

Ecco un esempio di come questo approccio viene applicato al mio scenario originale:

public abstract class DocumentTests : TestBase
{
    [TestClass()]
    public sealed class CheckInShould : DocumentTests
    {
        [TestMethod()]
        public void ThrowExceptionWhenUserIsNotAuthorized()
        {
        }
    }

    [TestClass()]
    public sealed class CheckOutShould : DocumentTests
    {
        [TestMethod()]
        public void ThrowExceptionWhenUserIsNotAuthorized()
        {
        }
    }
}

Ciò risulta nei seguenti test che appaiono nella lista dei test:

DocumentTests+CheckInShould.ThrowExceptionWhenUserIsNotAuthorized
DocumentTests+CheckOutShould.ThrowExceptionWhenUserIsNotAuthorized

Man mano che vengono aggiunti più test, questi vengono facilmente raggruppati e ordinati per nome della classe, il che mantiene anche tutti i test per la classe sotto test elencati insieme.

Un altro vantaggio di questo approccio che ho imparato a sfruttare è la natura orientata agli oggetti di questa struttura che significa che posso definire il codice all'interno dei metodi di avvio e pulizia della classe base DocumentTests che saranno condivisi anche da tutte le classi nidificate come all'interno di ogni classe annidata per i test che contiene.

    
risposta data 03.02.2012 - 03:37
fonte
16

È raro, ma a volte ha senso avere più classi di test per una determinata classe in prova. Normalmente lo farei quando sono richieste impostazioni diverse e condiviso su un sottoinsieme dei test.

    
risposta data 13.12.2011 - 16:14
fonte
14

Non riesco davvero a vedere alcun motivo convincente per cui dividere un test per una singola classe in più classi di test. Dal momento che l'idea guida dovrebbe essere quella di mantenere la coesione a livello di classe, dovresti sforzarti anche a livello di test. Solo alcuni motivi casuali:

  1. Non è necessario duplicare (e gestire più versioni di) codice di installazione
  2. Più facile eseguire tutti i test per una classe da un IDE se sono di gruppo in una classe di test
  3. Risoluzione dei problemi più semplice con il mapping uno-a-uno tra classi di test e classi.
risposta data 13.12.2011 - 15:57
fonte
6

Se si è costretti a suddividere i test di unità per una classe su più file, ciò potrebbe indicare che la classe stessa è mal progettata. Non riesco a pensare a uno scenario in cui sarebbe più vantaggioso suddividere i test unitari per una classe che rispetti ragionevolmente il Principio di Responsabilità Unica e altre buone pratiche di programmazione.

Inoltre, avere nomi di metodo più lunghi nei test di unità è accettabile, ma se ti infastidisce, puoi sempre riconsiderare la convenzione di denominazione dei test unitari per abbreviare i nomi.

    
risposta data 13.12.2011 - 16:04
fonte
2

Uno dei miei argomenti contro la separazione dei test in più classi è che diventa più difficile per gli altri sviluppatori del team (specialmente quelli che non sono esperti di test) di individuare i test esistenti ( Gee, mi chiedo se è già un test per questo metodo? Mi chiedo dove sarebbe? ) e anche dove mettere nuovi test ( ho intenzione di scrivere un test per questo metodo in questa classe, ma non sono abbastanza sicuro se dovessi metterli in un nuovo file o in uno esistente? )

Nel caso in cui test diversi richiedano configurazioni drasticamente differenti, ho visto alcuni professionisti di TDD mettere il "fixture" o il "setup" in classi / file diversi, ma non i test stessi.

    
risposta data 13.12.2011 - 20:27
fonte
1

Prova a pensare il contrario per un minuto.

Perché dovresti avere una sola classe di test per classe? Stai facendo un test di classe o un test unitario? Dipendi anche dalle classi ?

Si suppone che un test unitario stia testando un comportamento specifico, in un contesto specifico. Il fatto che la tua classe abbia già un CheckIn significa che dovrebbe esserci un comportamento che ne ha bisogno in primo luogo.

Come pensi di questo pseudo-codice:

// check_in_test.file

class CheckInTest extends TestCase {
        /** @test */
        public function unauthorized_users_cannot_check_in() {
                $this->expectException();

                $document = new Document($unauthorizedUser);

                $document->checkIn();
        }
}

Ora non stai testando direttamente il metodo checkIn . Stai testando un comportamento invece (che è il check-in per fortuna;)), e se hai bisogno di refactificare Document e dividerlo in classi diverse, o unire un'altra classe, il tuo test i file sono ancora coerenti, hanno ancora senso dal momento che non cambierai mai la logica durante il refactoring, solo la struttura.

Un file di test per una classe rende semplicemente più difficile refactoring ogni volta che ne hai bisogno, e anche i test hanno meno senso in termini di dominio / logica del codice stesso.

    
risposta data 21.11.2017 - 18:30
fonte
0

In generale, consiglierei di pensare ai test come test di un comportamento della classe piuttosto che a un metodo. Cioè alcuni dei tuoi test potrebbero aver bisogno di chiamare entrambi i metodi sulla classe per testare alcuni comportamenti previsti.

Di solito inizio con una classe di test unitaria per classe di produzione, ma alla fine posso dividere la classe di test unitario in diverse classi di test in base al comportamento che sottopongono a test. In altre parole, raccomanderei di dividere la classe di test in CheckInShould e CheckOutShould, ma piuttosto di dividere per comportamento dell'unità in prova.

    
risposta data 14.12.2011 - 10:37
fonte
0

1. Considera cosa potrebbe andare storto

Durante lo sviluppo iniziale, sai bene cosa stai facendo e probabilmente entrambe le soluzioni funzioneranno correttamente.

Diventa più interessante quando un test fallisce dopo una modifica molto più tardi. Ci sono due possibilità:

  1. Hai seriamente rotto CheckIn (o CheckOut ). Anche in questo caso sia il file singolo che la soluzione a due file sono OK.
  2. Hai modificato sia CheckIn che CheckOut (e forse i loro test) in un modo che fa senso per ciascuno di loro, ma non per entrambi insieme. Hai rotto la coerenza della coppia. In tal caso, suddividere i test su due file renderà più difficile capisci il problema.

2. Considera quali test sono usati per

I test hanno due scopi principali:

  1. Controlla automaticamente che il programma funzioni correttamente. Se i test sono in un file o in più non ha importanza per questo scopo.
  2. Aiuta un lettore umano a dare un senso al programma. Questo sarà molto più facile se test per funzionalità strettamente correlate sono tenuti insieme, perché vedere la loro coerenza è una strada rapida per comprensione.

così?

Entrambe queste prospettive suggeriscono che tenere insieme i test può aiutare, ma non farà male.

    
risposta data 21.11.2017 - 17:35
fonte

Leggi altre domande sui tag