Sta spiando le cattive pratiche di classe testate?

10

Sto lavorando a un progetto in cui le chiamate interne alla classe sono normali, ma i risultati sono molte volte valori semplici. Esempio ( codice non reale ):

public boolean findError(Set<Thing1> set1, Set<Thing2> set2) {
  if (!checkFirstCondition(set1, set2)) {
    return false;
  }
  if (!checkSecondCondition(set1, set2)) {
    return false;
  }
  return true;
}

Scrivere test di unità per questo tipo di codice è davvero difficile, in quanto voglio solo testare il sistema di condizioni e non l'implementazione delle condizioni effettive. (Lo faccio in test separati.) In effetti sarebbe meglio se passassi le funzioni che implementano le condizioni e nei test fornisco semplicemente un po 'di finzione. Il problema con questo approccio è la rumorosità: usiamo molto i generici .

Una soluzione di lavoro; tuttavia, è rendere l'oggetto testato spy e prendere in giro le chiamate alle funzioni interne.

systemUnderTest = Mockito.spy(systemUnderTest);
doReturn(true).when(systemUnderTest).checkFirstCondition(....);

La preoccupazione qui è che l'implementazione del SUT è effettivamente cambiata e potrebbe essere problematico mantenere i test in sincronia con l'implementazione. È vero? C'è la migliore pratica per evitare questo caos di chiamate al metodo interno?

Si noti che stiamo parlando di parti di un algoritmo, quindi suddividerlo in più classi potrebbe non essere una decisione desiderata.

    
posta allprog 25.09.2013 - 11:49
fonte

4 risposte

11

I test unitari dovrebbero trattare le classi che testano come scatole nere. L'unica cosa che conta è che i suoi metodi pubblici si comportano come è previsto. Il modo in cui la classe raggiunge questo risultato attraverso lo stato interno e i metodi privati non ha importanza.

Quando ritieni che sia impossibile creare test significativi in questo modo, è un segnale che le tue classi sono troppo potenti e fanno troppo. Dovresti considerare di spostare alcune delle loro funzionalità in classi separate che possono essere testate separatamente.

    
risposta data 25.09.2013 - 14:53
fonte
4

Se sia findError() che checkFirstCondition() ecc. sono metodi pubblici della classe, quindi findError() è effettivamente una facciata per funzionalità già disponibile dalla stessa API. Non c'è niente di sbagliato in questo, ma significa che devi scrivere dei test che sono molto simili ai test già esistenti. Questa duplicazione riflette semplicemente la duplicazione nella tua interfaccia pubblica. Non c'è motivo per trattare questo metodo in modo diverso dagli altri.

    
risposta data 25.09.2013 - 11:58
fonte
3

I test unitari dovrebbero testare il contratto; è l'unica cosa importante, per loro. Testare qualsiasi cosa che non fa parte del contratto non è solo una perdita di tempo, è una potenziale fonte di errore. Ogni volta che vedi uno sviluppatore che cambia i test quando cambia un dettaglio di implementazione, le campane di allarme dovrebbero suonare; lo sviluppatore potrebbe essere (intenzionalmente o no) nascondere i propri errori. Intenzionalmente i dettagli di implementazione dei test impongono questa cattiva abitudine, rendendo più probabile la mascheratura degli errori.

Le chiamate interne sono un dettaglio di implementazione e dovrebbero essere solo interessanti per misurare performance . Che di solito non è il lavoro dei test unitari.

    
risposta data 25.09.2013 - 13:56
fonte
2

In primo luogo, mi chiedo cosa è difficile da testare sulla funzione di esempio che hai scritto? Per quanto posso vedere, puoi semplicemente passare in vari input e verificare che venga restituito il valore booleano corretto. Cosa mi manca?

Per quanto riguarda le spie, il tipo di test cosiddetto "white-box" che usa spie e mazze è un ordine di grandezza più lavoro da scrivere, non solo perché c'è molto più codice di prova da scrivere, ma ogni volta l'implementazione è cambiata, è necessario anche modificare i test (anche se l'interfaccia rimane la stessa). E questo tipo di test è anche meno affidabile rispetto ai test black-box, perché è necessario assicurarsi che tutto quel codice di prova extra sia corretto, e mentre si può credere che i test dell'unità black-box falliranno se non corrispondono all'interfaccia non ci si può fidare del codice che esagera i mock perché a volte il test non prova nemmeno molto codice reale - solo mock. Se i mock non sono corretti, probabilmente i test avranno successo, ma il tuo codice è ancora rotto.

Chiunque abbia esperienza con i test white-box può dire che è un rompicoglioni da scrivere e mantenere. In combinazione con il fatto che sono meno affidabili, il test white-box è semplicemente di gran lunga inferiore nella maggior parte dei casi.

    
risposta data 21.06.2014 - 06:09
fonte

Leggi altre domande sui tag