Unità che collauda componenti interni


14

In che misura testate i componenti interni / privati ​​di una classe / modulo / pacchetto / ecc? Li provi o provi semplicemente l'interfaccia con il mondo esterno? Un esempio di questi metodi interni è privato.

Ad esempio, immagina un parser di discesa ricorsivo , che ha diverse procedure interne (funzioni / metodi) chiamate da una procedura centrale. L'unica interfaccia con il mondo esterno è la procedura centrale, che prende una stringa e restituisce le informazioni analizzate. Le altre procedure analizzano parti diverse della stringa e vengono chiamate dalla procedura centrale o da altre procedure.

Naturalmente, è necessario testare l'interfaccia esterna chiamandola con stringhe di esempio e confrontandola con l'output analizzato a mano. Ma per quanto riguarda le altre procedure? Li testeresti individualmente per verificare che analizzino correttamente le loro sottostringhe?

Mi vengono in mente alcuni argomenti:

Pro :

  1. Più test è sempre meglio e questo può aiutare ad aumentare la copertura del codice
  2. Alcuni componenti interni potrebbero essere difficili da fornire input specifici (ad esempio casi limite) fornendo input all'interfaccia esterna
  3. Test più chiari. Se un componente interno ha un bug (risolto), un caso di test per quel componente chiarisce che il bug era in quel componente specifico

Contro :

  1. Il refactoring diventa troppo doloroso e richiede tempo. Per modificare qualsiasi cosa, è necessario riscrivere i test unitari, anche se gli utenti dell'interfaccia esterna non sono interessati
  2. Alcuni linguaggi e framework di test non lo consentono

Quali sono le tue opinioni?


possibile duplicato o sovrapposizione significativa con: programmers.stackexchange.com/questions/10832/…
azheglov

Risposte:


8

Caso: un "modulo" (in senso lato, cioè qualcosa che ha un'interfaccia pubblica e possibilmente anche alcune parti interne private) ha una logica complicata / coinvolta al suo interno. Testare solo l'interfaccia del modulo sarà una sorta di test di integrazione in relazione alla struttura interna del modulo, e quindi nel caso in cui venga rilevato un errore, tale test non localizzerà l'esatta parte interna / componente responsabile del guasto.

Soluzione: trasforma le parti interne complicate in moduli stessi, testali l'unità (e ripeti questi passaggi se sono troppo complicati) e importa nel tuo modulo originale. Ora hai solo una serie di moduli abbastanza semplici da testare l'unit (entrambi controllano che il comportamento sia corretto e correggano gli errori) facilmente, e questo è tutto.

Nota:

  • non sarà necessario modificare nulla nei test dei (precedenti) "sottomoduli" del modulo quando si modifica il contratto del modulo, a meno che i "sottomoduli" non offrano più servizi sufficienti per adempiere al contratto nuovo / modificato.

  • nulla sarà inutilmente reso pubblico, cioè il contratto del modulo sarà mantenuto e l'incapsulamento mantenuto.

[Aggiornare]

Per testare alcune logiche interne intelligenti nei casi in cui è difficile mettere le parti interne dell'oggetto (intendo membri non i moduli / pacchetti importati privatamente) nello stato appropriato semplicemente alimentando gli input tramite l'interfaccia pubblica dell'oggetto:

  • basta avere un po 'di codice di prova con un amico (in termini di C ++) o un pacchetto (Java) per accedere alle viscere che in realtà impostano lo stato dall'interno e testano il comportamento come si desidera.

    • questo non interromperà nuovamente l'incapsulamento fornendo al contempo un facile accesso diretto agli interni a scopo di test - basta eseguire i test come una "scatola nera" e compilarli nelle build di rilascio.

e il layout dell'elenco sembra essere un po 'rotto; (
mlvljr

1
Buona risposta. In .NET è possibile utilizzare l' [assembly: InternalsVisibleTo("MyUnitTestAssembly")]attributo in AssemblyInfo.csper testare gli interni. Sembra imbrogliare però.
Nessuno il

@rmx Una volta che qualcosa soddisfa tutti i criteri necessari, non è necessario imbrogliare, anche se ha qualcosa in comune con i trucchi reali. Ma il tema dell'accesso tra moduli / intra-modulo è davvero un po 'inventato nei moderni linguaggi tradizionali.
mlvljr,

2

L'approccio al codice basato su FSM è leggermente diverso da quello utilizzato tradizionalmente. È molto simile a quanto descritto qui per i test hardware (che in genere è anche un FSM).

In breve, si crea una sequenza di input di test (o un insieme di sequenze di input di test) che non dovrebbe solo produrre un determinato output, ma anche quando si produce un output "cattivo" particolare consente di identificare il componente non funzionante in base alla natura del fallimento. L'approccio è abbastanza scalabile, più tempo dedichi alla progettazione del test, migliore sarà il test.

Questo tipo di test è più vicino a quelli che vengono chiamati "test funzionali" ma elimina la necessità di cambiare test ogni volta che si tocca leggermente un'implementazione.


2

Beh, dipende :-). Se stai seguendo un approccio BDD (Behavior Driven Development) o ATDD (Acceptance Test Driven Development), allora testare l'interfaccia pubblica va bene (purché lo test in modo esaustivo con input variabili. L'implementazione sottostante, ad esempio i metodi privati ​​non lo è davvero importante.

Tuttavia, dire che si desidera che parte dell'algoritmo venga eseguito entro un determinato intervallo di tempo o lungo una certa curva bigO (ad esempio nlogn), quindi sì testando le singole parti contano. Alcuni lo chiamerebbero più un tradizionale approccio TDD / Unit Test.

Come per tutto, YMMV


1

Dividerlo in più parti con un significato funzionale, ad esempio ParseQuotedString(), ParseExpression(), ParseStatement(), ParseFile()e li pubblica fare. Quanto è probabile che la tua sintassi cambi così tanto da renderli irrilevanti?


1
questo approccio porta facilmente a incapsulamenti più deboli e interfacce più grandi e più difficili da usare / comprendere.
Sara,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.