Dovresti testare indirettamente i metodi privati dai test dei metodi pubblici?

3

Sembra essere opinione comune che si debbano solo testare metodi pubblici per una classe o un modulo e non per metodi privati. Se i metodi privati stanno facendo correttamente il loro lavoro, questo dovrebbe riflettersi nei metodi pubblici.

Detto questo, testando un metodo pubblico che effettua chiamate interne a metodi privati, dovresti testare per cose che potrebbero accadere in quel metodo privato? In sostanza, testare indirettamente il metodo privato?

Esempio semplificato:

// Basic JavaScript module
let MyModule = function() {};
MyModule.prototype.taco = function() {
  ...
  burrito();
  ...
}

// Private function burrito
let burrito = function(){
  ...
  if (somethingHappened) {
    throw new Error('Oops!');
  }
  ...
}

In questo caso, il metodo privato burrito viene chiamato all'interno del metodo pubblico taco . In alcune situazioni, burrito può generare un errore.

Devo scrivere un test per taco dove sto testando per vedere se un errore viene mai generato in determinate situazioni? Non sto testando tecnicamente per vedere se il metodo taco stesso genera un errore, ma piuttosto se il metodo privato sottostante genera un errore.

È qualcosa che ritengo sia importante testare, ma dal momento che è forse una cattiva pratica testare direttamente i metodi privati, è questo il modo giusto per testarlo? O non dovrei provarlo affatto? Voglio solo assicurarmi che determinati errori vengano lanciati in modo prevedibile.

Ad esempio, se il metodo burrito effettua una richiesta http su qualche server API da qualche parte, ma il server non è in linea al momento, genera un errore. Sarebbe bello scrivere un test per garantire che quando l'endpoint del server è offline, venga generato l'errore corretto.

    
posta Jake Wilson 21.11.2016 - 08:40
fonte

3 risposte

4

I test unitari servono a molteplici scopi. In teoria dovresti mirare alla copertura del codice al 100%, ma in pratica questo non ha sempre senso (non è il miglior uso delle risorse disponibili). Se raggiungi una copertura del 100% del codice, implicitamente sono stati eseguiti tutti i percorsi del codice in tutti i metodi privati, incluso il tuo caso di errore.

Un altro uso per i test unitari è quello di documentare tutti i casi d'uso previsti dall'implementatore originale. Se vuoi capire le capacità e i limiti di una classe, leggere i test unitari dovrebbe dirlo in modo molto più efficace della lettura della classe stessa. In questo caso, se prevedi un caso d'uso in cui il consumatore della tua classe dovrebbe ricevere una risposta di errore, dovresti documentare questo caso d'uso includendo un test unitario per questo.

Non si tratta in realtà di ciò che è pubblico o di ciò che è privato, l'implementazione interna di una classe è autorizzata a cambiare, e se i test di unità passano ancora, nulla dovrebbe essere rotto da questo refactoring. Riguarda qual è il comportamento previsto della classe dal punto di vista del resto del sistema e assicurando che questo contratto non venga interrotto dalle modifiche apportate alla classe.

    
risposta data 21.11.2016 - 09:12
fonte
0

Costruisci i tuoi casi di test modificando i parametri di input o modificando alcuni stati pubblici di classe sotto test.
Ogni caso di test eseguirà diversi rami della tua logica.
Nell'esempio, l'eccezione verrà generata se somethingHappened - si tratta di uno stato pubblico della classe o del parametro assegnato al metodo? Che cosa succede quando if(somethingHappened) restituirà true ?

Il vantaggio del test dell'unità "black boxing" è che quando hai coperto i test puoi facilmente refactoring qualsiasi metodo privato senza riscrivere i test unitari.

Se le eccezioni non possono essere ottenute attraverso l'API pubblica del metodo / classe, allora come può essere ottenuto nella produzione?

    
risposta data 28.11.2016 - 23:35
fonte
0

Sì, dovresti verificare indirettamente i percorsi del codice che attraversano le funzioni private. Posso dimostrartelo con un esempio più estremo:

private int privateFunction(int input)
{
    return [some int based on the input];
}

public int publicFunction(int input)
{
    return privateFunction(input);
}

Qualsiasi test significativo deve testare la logica di privateFunction in questo caso. Lo stesso è vero nel tuo caso più generale.

    
risposta data 28.11.2016 - 23:53
fonte

Leggi altre domande sui tag