Qual è la migliore pratica dei metodi di prova che si chiamano a vicenda?

6

Dire che ho un metodo A che chiama un metodo B e fa una cosa aggiuntiva. Il metodo B si comporta in modo diverso in 10 casi diversi e ho un ampio test unitario che lo descrive. E ora voglio testare il mio metodo A. Qual è la migliore pratica per farlo? Devo eseguire lo stesso test con tutti i 10 casi per il metodo A per verificare che venga eseguito il metodo corretto? Sembra un eccesso. O forse dovrei testare solo uno di quei casi? Ma c'è il rischio che in futuro scriverò un metodo che sarà abbastanza simile a B e che si verificherà un errore che non verrà rilevato dal test. O forse ci sono alcuni modi per verificare che il mio metodo B sia stato attivato semplicemente registrando il suo nome? Forse è un problema abbastanza semplice, ma sono nuovo nel test delle unità e non ho trovato alcuna risposta chiara.

    
posta Karol Selak 23.05.2018 - 12:43
fonte

2 risposte

7

La tua domanda non mi è completamente chiara, quindi sto facendo le seguenti ipotesi:

  1. Metodo A e B sono entrambi "pubblici",
  2. Metodo B ha 10 percorsi di esecuzione attraverso di esso e 10 casi di test che coprono quei percorsi,
  3. Metodo A chiama B tramite almeno uno dei suoi percorsi di esecuzione.
  4. Ci sono percorsi attraverso B che non vengono utilizzati da A .

Inoltre, dovrò ignorare "Ma c'è il rischio che in futuro scriverò un metodo che sarà abbastanza simile a B e si verificherà un errore che non verrà rilevato dal test" perché non vedo come sia correlato alla tua domanda o al test A e B .

La prima cosa che dirò è non provare a prendere in giro B, a meno che non sia assolutamente necessario . Questo è un classico errore di test newbie che molte persone fanno. Devi solo isolare A da B se quest'ultimo ha effetti collaterali o è lento. Se prendi in giro B , dovrai semplicemente testare A rispetto alla funzionalità di simulazione, piuttosto che la vera funzionalità di B e di conseguenza mancherai bug.

La prossima cosa da porsi è: puoi riutilizzare alcuni di quei casi di test B per testare insieme A e B ? Quindi se hai quattro percorsi attraverso B che sono usati da A , prova quei percorsi testando A . C'è comunque un grosso avvertimento. Ricorda che ognuno di questi test si accoppierà strettamente alla funzionalità esistente di A e B . Se in seguito cambi A per disporre di tale funzionalità, ad esempio, potresti non testare quella parte di B . Oppure i cambiamenti potrebbero interrompere i test solo perché il test è ora fragile, non perché hai rotto la funzionalità, ecc. Come sempre, c'è un equilibrio tra principio (prova tutto!) E pragmatismo (devo spedirlo questo giorno per per avere valore in quello che sto facendo).

    
risposta data 23.05.2018 - 13:34
fonte
1

I'm new in unit testing and I haven't found any clear answer.

In molti casi, non c'è una risposta chiara: solo diversi compromessi che è necessario valutare per le proprie circostanze.

Il discorso che devi guardare è I test integrati sono una truffa , di J.B. Rainsberger. Vedi anche il suo blog .

Potresti anche voler esaminare un po 'di saggezza da Kent Beck (2008)

I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence

Una delle motivazioni dei test unitari è che limitano la scelta delle implementazioni; l'idea è che i test ti danno un segnale che i tuoi cambiamenti hanno prodotto un cambiamento osservabile nel comportamento del tuo sistema.

Quindi, se hai un sacco di test per B , e supponi che quelli coprano anche A , va bene fino al punto che qualcuno decide di cambiare l'implementazione di A .

Se A is-a B; il che significa che si dovrebbe essere in grado di sostituire un B con un A e avere ancora un programma corretto, quindi un trucco potenzialmente utile è scrivere una suite di test che viene invocata sia su A che su B.

void verifyThatItWorks(SystemUnderTest sut) {
    // this is where the implementation of the test lives
    // including the setup, activity, and verification
    // aka arrange, act, assert
}

@Test
void testB () {
    verifyThatItWorks(new B())
}

@Test
void testA () {
    verifyThatItWorks(new A())
}

In alcuni linguaggi di programmazione ci sono comodità che ti permettono di gestire copie parallele di grandi suite di test

abstract class AbstractSpecification {
    SystemUnderTest sut

    protected AbstractSpecification(SystemUnderTest sut) {
        this.sut = sut;
    }

    @Test behavior1 () {...}
    @Test behavior2 () {...} 
    // ...
}

class BSpecification extends AbstractSpecification {
    BSpecification () { super (new B()); }
}
class ASpecification extends AbstractSpecification {
    ASpecification () { super(new A()); }
}
    
risposta data 23.05.2018 - 15:11
fonte

Leggi altre domande sui tag