I test unitari talvolta interrompono l'incapsulamento? [duplicare]

25

Sento molto spesso quanto segue: "Se vuoi testare metodi privati, è meglio metterlo in un'altra classe ed esporlo."

Anche se a volte è così e abbiamo un concetto nascosto nella nostra classe, altre volte si finisce con classi che hanno gli stessi attributi (o, peggio, ogni attributo di una classe diventa un argomento su un metodo nell'altra classe ) ed espone funzionalità che sono, in effetti, dettagli di implementazione.

Specialmente su TDD, quando refactate una classe con metodi pubblici da una precedente classe testata, quella classe ora fa parte dell'interfaccia, ma non ha test (dato che l'hai rifatta, ed è un dettaglio di implementazione).

Ora, potrei non trovare una risposta più ovvia, ma se la mia risposta è "corretta", ciò significa che a volte i test di unità di scrittura possono interrompere l'incapsulamento e dividere la stessa responsabilità in classi diverse.

Un semplice esempio potrebbe testare un metodo setter quando un getter non è effettivamente necessario per qualcosa nel codice reale.

Per favore, quando non si forniscono risposte semplici a casi specifici che potrei aver scritto. Piuttosto, cerca di spiegare più del caso generico e dell'approccio teorico. E questo non è specifico per la lingua.

Grazie in anticipo.

EDIT: la risposta fornita da Matthew Flynn è stata davvero perspicace, ma non ha risposto alla domanda. Anche se ha fatto il punto giusto di non testare metodi privati o di estrapolarli perché in realtà sono altre preoccupazioni e responsabilità (o almeno questo era ciò che ho potuto capire dalla sua risposta), penso che ci siano situazioni in cui i test unitari sono privati i metodi sono utili Il mio esempio principale è quando si ha una classe che ha una responsabilità ma l'output (o input) che dà (prende) è solo complesso. Ad esempio, una funzione di hashing. Non c'è un buon modo per rompere una funzione di hashing e mantenere la coesione e l'incapsulamento. Tuttavia, testare una funzione di hashing può essere davvero difficile, dal momento che è necessario calcolare a mano (non è possibile utilizzare il calcolo del codice per verificare il calcolo del codice!) L'hashing e testare più casi in cui l'hash cambia. In questo modo (e questa potrebbe essere una domanda che vale il suo stesso argomento), penso che il metodo di test privato sia il modo migliore per gestirlo. Ora, non sono sicuro se dovrei fare un'altra domanda, o chiedere qui, ma c'è un modo migliore per testare un output così complesso (input)?

OBS: Per favore, se pensi che dovrei fare un'altra domanda su quell'argomento, lascia un commento. :)

EDIT2: Ho accettato una risposta come mia corretta perché mi ha fatto pensare e decidere la mia linea d'azione, anche se non ha risposto completamente alla mia ricerca. Ma per quelli che affrontano lo stesso problema di me (una classe coesiva che cambierà insieme, ma è ancora troppo difficile da testare da sola), dirò cosa ho fatto e perché. Ho deciso che l'output di quella classe è semplicemente troppo difficile da testare su un computer, quindi non l'ho testato. Avrei potuto usare un framework per testare i suoi metodi privati (che sarebbe la migliore idea, penso), ma non volevo arrivare a quel punto. Se ti stai chiedendo cosa sia coesivo e rispetti SRP, ed è ancora troppo difficile da testare su un computer, darò alcuni esempi: generazione di heightmap, funzioni di hash, generazione di musica procedurale (puoi testare alcune unità, ma l'unità di livello più alto è semplicemente troppo soggettiva).

    
posta user1288851 21.10.2012 - 01:56
fonte

5 risposte

37

Quando ero più giovane e sollevavo questa domanda, mi è stato detto che in realtà non ho bisogno di scrivere test unitari per metodi privati. Sorprendentemente, questo si è rivelato vero.

Se segui un approccio comportamentale ai test unitari, questo ha perfettamente senso: i tuoi metodi pubblici sono ciò che viene chiesto di fare cose. Il fatto che chiamino metodi privati interni è ortogonale al loro comportamento esteriore. Poiché l'altro metodo è privato / incapsulato, in realtà è parte dell'unità sottoposta a test.

Potresti chiedere che se più metodi nella tua classe chiamano tutti un singolo metodo privato, quindi non dovresti provare che quel metodo funziona? La risposta qui è sì, ma non è resa evidente da un test unitario del metodo privato, ma piuttosto dai test unitari dei metodi pubblici che lo chiamano. Se i metodi pubblici funzionano e chiamano un metodo privato, anche il metodo privato deve funzionare.

    
risposta data 21.10.2012 - 03:27
fonte
10

Quando senti il bisogno di testare metodi privati, stai semplicemente sbagliando. potrebbe essere in grado di calcolare un complesso algoritmo di un metodo privato in una classe separata, "più pubblica", e utilizzare questa classe nel metodo privato originale. Ma il punto è che questo non rompe ancora l'incapsulamento.

Se rendi qualcosa di privato, non fa parte dell'API della classe, semplicemente non esiste per il mondo esterno. Se succede qualcosa che è davvero importante, e dovrebbe essere testato secondo te, ma non può essere rilevato correttamente attraverso l'API, allora qualcosa nel tuo progetto API è rotto. Devi decidere se qualcosa è importante o se è un dettaglio di implementazione. Non può essere entrambi.

    
risposta data 21.10.2012 - 13:11
fonte
6

Anche se non sono in disaccordo con le risposte che dicono "Privato è privato" e nella maggior parte dei casi questo è vero, non lo è sempre.

Se il tuo codice prodotto fa parte di un prodotto certificato per la sicurezza (ad esempio aerospaziale, medico, automobilistico), allora troverai requisiti di copertura del test e delizie come Copertura delle condizioni / decisione modificata

Dicendo all'Ente di certificazione che non hai testato gli interni della tua classe, perché privato è privato non otterrai un segno di spunta nella casella.

Solo il test dell'interfaccia pubblica di una classe è Black Box test ... e fino a quando i risultati sono OK, quindi (nella maggior parte dei casi) è sufficiente. Ma a volte , devi eseguire test White Box e convalidare i dadi e i bulloni al suo interno.

Sottolineo a volte ma (come con la maggior parte delle "regole") non dico mai "mai testare il privato".

    
risposta data 06.11.2012 - 12:41
fonte
4

Prima di tutto, non codice, ma il comportamento di classe è ciò che viene testato. Il codice è solo un modo per far funzionare la classe come previsto, ma il codice non è richiesto affatto :) Sarebbe davvero meglio far funzionare la tua classe senza scrivere codice (OFF TOPIC).

I metodi pubblici chiamano metodi privati, quindi i test dei metodi pubblici dovrebbero sempre essere sufficienti. Quando si utilizza TDD, è necessario scrivere test prima dell'implementazione, quindi è impossibile testare i metodi privati.

Per quanto riguarda i software critici per la salute e cose del genere, la copertura del 100% dei percorsi testando metodi privati non garantisce che il software testato funzioni correttamente nel 100% dei casi. Puoi essere sicuro solo dopo aver verificato tutti i possibili dati di input su tutti i possibili stati dell'oggetto. Fare questo solo per i metodi pubblici dovrebbe coprire tutti i percorsi in metodi privati.

    
risposta data 21.08.2013 - 13:31
fonte
0

Talvolta nei test è possibile avere accesso ai membri protetti / privati.

Supponiamo che tu abbia una funzione di inizializzazione - vuoi testare che certe variabili interne siano state impostate. Il modo più semplice per farlo è che il tuo framework di test abbia accesso a tali variabili, generalmente la classe di test deriva dalla classe o c'è qualche trucco per convertire protected- > public durante il test.

Potresti aggiungere funzioni public () per restituire il valore di tutte le variabili protette, ma ciò potrebbe spezzare l'incapsulamento piuttosto di più.

    
risposta data 21.10.2012 - 05:15
fonte