TDD Red-Green-Refactor e if / how per testare metodi che diventano privati


91

per quanto ne capisco, la maggior parte delle persone sembra concordare sul fatto che i metodi privati ​​non dovrebbero essere testati direttamente, ma piuttosto attraverso qualunque metodo pubblico li chiama. Vedo il loro punto, ma ho qualche problema con questo quando provo a seguire le "Tre Leggi del TDD" e uso il ciclo "Rosso - verde - Rifattore". Penso che sia meglio spiegato da un esempio:

In questo momento, ho bisogno di un programma in grado di leggere un file (contenente dati separati da tabulazioni) e filtrare tutte le colonne che contengono dati non numerici. Immagino che probabilmente ci siano già alcuni semplici strumenti disponibili per farlo, ma ho deciso di implementarlo da zero, soprattutto perché ho pensato che potesse essere un progetto bello e pulito per me fare un po 'di pratica con TDD.

Quindi, per prima cosa, "metto il cappello rosso", cioè ho bisogno di un test che fallisca. Ho pensato, avrò bisogno di un metodo che trovi tutti i campi non numerici in una linea. Quindi scrivo un semplice test, ovviamente non riesce a compilare immediatamente, quindi inizio a scrivere la funzione stessa e dopo un paio di cicli avanti e indietro (rosso / verde) ho una funzione operativa e un test completo.

Successivamente, continuo con una funzione, "gatherNonNumericColumns" che legge il file, una riga alla volta, e chiama la mia funzione "findNonNumericFields" su ciascuna riga per raccogliere tutte le colonne che alla fine devono essere rimosse. Un paio di cicli rosso-verde, e ho finito, avendo di nuovo, una funzione di lavoro e un test completo.

Ora, immagino che dovrei refactoring. Poiché il mio metodo "findNonNumericFields" è stato progettato solo perché ho pensato che ne avrei avuto bisogno durante l'implementazione di "gatherNonNumericColumns", mi sembra ragionevole lasciare che "findNonNumericFields" diventi privato. Tuttavia, ciò interromperebbe i miei primi test, poiché non avrebbero più accesso al metodo che stavano testando.

Quindi, finisco con un metodo privato e una suite di test che lo prova. Dato che così tante persone consigliano che i metodi privati ​​non dovrebbero essere testati, mi sento come se mi fossi dipinto in un angolo qui. Ma dove ho fallito esattamente?

Ho capito che avrei potuto iniziare a un livello più alto, scrivendo un test che mette alla prova quello che alla fine diventerà il mio metodo pubblico (cioè findAndFilterOutAllNonNumericalColumns), ma questo sembra in qualche modo in contrasto con l'intero punto del TDD (almeno secondo lo zio Bob) : Che dovresti passare costantemente tra i test di scrittura e il codice di produzione e che in qualsiasi momento, tutti i tuoi test hanno funzionato nell'ultimo minuto circa. Perché se inizio scrivendo un test per un metodo pubblico, ci saranno diversi minuti (o ore o addirittura giorni in casi molto complessi) prima che ottenga tutti i dettagli nei metodi privati ​​per funzionare in modo che il test collauda il pubblico il metodo passa.

Quindi che si fa? TDD (con il rapido ciclo rosso-verde del refattore) non è semplicemente compatibile con i metodi privati? O c'è un difetto nel mio design?



2
O queste due funzionalità sono abbastanza diverse da essere diverse unità - nel qual caso i metodi privati ​​dovrebbero probabilmente essere nelle loro classi - o sono la stessa unità nel qual caso non vedo perché stai scrivendo test per comportamento interno all'unità. Per quanto riguarda il penultimo paragrafo, non vedo il conflitto. Perché dovresti scrivere un intero metodo privato complesso per passare un caso di prova? Perché non cacciarlo gradualmente con il metodo pubblico o iniziare con esso in linea e poi estrarlo?
Ben Aaronson,

26
Perché le persone prendono idiomi e cliché da libri di programmazione e blog come linee guida reali su come programmare è al di là di me.
AK_15

7
Non mi piace il TDD proprio per questo motivo: se ti trovi in ​​una nuova area, farai molto lavoro extra mentre provi a scoprire come dovrebbe essere l'architettura e come funzionano determinate cose. D'altra parte: se ti trovi in ​​un'area con cui hai già esperienza, allora ci sarà il vantaggio di scrivere prima i test oltre a infastidirti perché intellisense non capisce perché scrivi codice non compilabile. Sono un fan molto più grande di pensare al design, di scriverlo e quindi di testarlo.
Jeroen Vannevel,

1
"la maggior parte delle persone sembra concordare sul fatto che i metodi privati ​​non debbano essere testati direttamente" - no, testare un metodo direttamente se ha senso farlo. Nascondilo come privatese avesse senso farlo.
osa,

Risposte:


44

unità

Penso di poter individuare esattamente dove è iniziato il problema:

Ho pensato, avrò bisogno di un metodo che trovi tutti i campi non numerici in una linea.

Questo dovrebbe essere immediatamente seguito chiedendoti "Sarà un'unità testabile separata gatherNonNumericColumnso parte della stessa?"

Se la risposta è " sì, separata ", allora il tuo modo di agire è semplice: quel metodo deve essere pubblico su una classe appropriata, quindi può essere testato come un'unità. La tua mentalità è qualcosa del tipo "Ho bisogno di provare a scovare un metodo e devo anche provare a scacciare un altro metodo"

Da quello che dici, hai capito che la risposta è " no, parte della stessa ". A questo punto, il tuo piano non dovrebbe più essere quello di scrivere e testare completamente, findNonNumericFields quindi scrivere gatherNonNumericColumns. Invece, dovrebbe essere semplicemente scrivere gatherNonNumericColumns. Per ora, findNonNumericFieldsdovrebbe essere solo una parte probabile della destinazione che hai in mente quando scegli il tuo prossimo test case rosso e fai il refactoring. Questa volta la tua mentalità è "Ho bisogno di provare a scovare un metodo, e mentre lo faccio dovrei tenere a mente che la mia implementazione completata includerà probabilmente questo altro metodo".


Mantenere un ciclo breve

Fare quanto sopra non dovrebbe portare ai problemi che descrivi nel tuo penultimo paragrafo:

Perché se inizio scrivendo un test per un metodo pubblico, ci saranno diversi minuti (o ore o addirittura giorni in casi molto complessi) prima che ottenga tutti i dettagli nei metodi privati ​​per funzionare in modo che il test collauda il pubblico il metodo passa.

In nessun caso questa tecnica richiede di scrivere un test rosso che diventerà verde solo quando si implementa l'intero findNonNumericFieldsda zero. Molto più probabilmente, findNonNumericFieldsinizierà come parte del codice in linea nel metodo pubblico che stai testando, che verrà creato nel corso di diversi cicli e infine estratto durante un refactoring.


Roadmap

Per fornire una tabella di marcia approssimativa per questo esempio particolare, non conosco i casi di test esatti che hai usato, ma dire che stavi scrivendo gatherNonNumericColumnscome metodo pubblico. Quindi molto probabilmente i casi di test sarebbero gli stessi per cui hai scritto findNonNumericFields, ognuno usando una tabella con una sola riga. Quando lo scenario di una riga è stato completamente implementato e si desidera scrivere un test per forzare l'estrazione del metodo, si scriverà un caso a due righe che richiederebbe di aggiungere l'iterazione.


2
Penso che questa sia la risposta proprio qui. Adottando TDD in un ambiente OOP, mi sono spesso trovato in difficoltà a superare i miei istinti dal basso verso l'alto. Sì, le funzioni dovrebbero essere piccole, ma dopo il refactoring. Prima, possono essere enormi monoliti. +1
João Mendes,

2
@ JoãoMendes Bene, non sono sicuro che dovresti arrivare allo stato di un enorme monolite prima del refactoring, specialmente su cicli RGR molto brevi. Ma sì, all'interno di un'unità testabile, lavorare dal basso verso l'alto può portare ai problemi descritti dall'OP.
Ben Aaronson,

1
OK, penso di aver capito dove è andato storto ora. Grazie mille a tutti voi (contrassegnate questa come risposta, ma la maggior parte delle altre risposte sono ugualmente utili)
Henrik Berg

66

Molte persone pensano che il test unitario sia basato sul metodo; non è. Dovrebbe essere basato sull'unità più piccola che abbia senso. Per la maggior parte delle cose questo significa che la classe è ciò che dovresti testare come entità intera. Non metodi individuali su di esso.

Ora ovviamente chiamerai metodi sulla classe, ma dovresti pensare ai test come applicabili all'oggetto black box che hai, quindi dovresti essere in grado di vedere che qualunque operazione logica fornisca la tua classe; queste sono le cose che devi testare. Se la tua classe è così grande che l'operazione logica è troppo complessa, allora hai un problema di progettazione che dovrebbe essere risolto per primo.

Una classe con un migliaio di metodi può sembrare testabile, ma se provi solo ciascun metodo singolarmente non stai davvero testando la classe. Alcune classi potrebbero richiedere di trovarsi in un determinato stato prima di chiamare un metodo, ad esempio una classe di rete che necessita di una connessione impostata prima di inviare i dati. Il metodo di invio dati non può essere considerato indipendentemente dall'intera classe.

Quindi dovresti vedere che i metodi privati ​​sono irrilevanti per il test. Se non puoi esercitare i tuoi metodi privati ​​chiamando l'interfaccia pubblica della tua classe, quei metodi privati ​​sono inutili e non verranno comunque utilizzati.

Penso che molte persone provino a trasformare i metodi privati ​​in unità testabili perché sembra facile eseguire test per loro, ma questo porta la granularità del test troppo lontano. Dice Martin Fowler

Anche se inizio con l'idea che l'unità sia una classe, spesso prendo un gruppo di classi strettamente correlate e le trattano come una singola unità

che ha molto senso per un sistema orientato agli oggetti, gli oggetti progettati per essere unità. Se vuoi testare singoli metodi, forse dovresti creare un sistema procedurale come C, o una classe composta interamente da funzioni statiche.


14
A me sembra che questa risposta ignori completamente l'approccio TDD nella domanda del PO. È solo una ripetizione del mantra "non testare i metodi privati", ma non spiega come TDD - che in realtà è basato sul metodo - potrebbe funzionare con un approccio unit test non basato sul metodo.
Doc Brown,

6
@DocBrown no, gli risponde completamente dicendo "non sovra-granualizzare" le tue unità e rendere la vita difficile per te stesso. TDD non è basato sul metodo, è basato sull'unità in cui un'unità ha qualunque senso. Se hai una libreria C, allora sì ogni unità sarà una funzione. Se hai una classe, l'unità è un oggetto. Come dice Fowler, a volte un'unità è composta da diverse classi strettamente correlate. Penso che molte persone considerino il test unitario metodo per metodo semplicemente perché alcuni strumenti stupidi generano stub basati su metodi.
gbjbaanb,

3
@gbjbaanb: prova a suggerire un test che consenta all'OP di implementare i suoi "campi non numerici in una riga" usando prima TDD puro, senza avere l'interfaccia pubblica della classe che intende scrivere già scritta.
Doc Brown,

8
Devo essere d'accordo con @DocBrown qui. Il problema dell'interrogatore non è che desidera testare la granularità più di quanto possa ottenere senza testare metodi privati. È che ha cercato di seguire un rigoroso approccio TDD e, senza pianificare in quanto tale, lo ha portato a colpire un muro dove improvvisamente scopre di avere un sacco di test per quello che dovrebbe essere un metodo privato. Questa risposta non aiuta a questo. È una buona risposta a qualche domanda, ma non questa.
Ben Aaronson,

7
@Matthew: Il suo errore è che ha scritto la funzione in primo luogo. Idealmente, avrebbe dovuto scrivere il metodo pubblico come codice spaghetti, quindi riformattarlo in una funzione privata nel ciclo dei refattori, non contrassegnarlo come privato nel ciclo dei refattori.
slebetman,

51

Il fatto che i tuoi metodi di raccolta dei dati siano abbastanza complessi da meritare i test e separati abbastanza dal tuo obiettivo primario da essere metodi propri piuttosto che parte di alcuni loop indica la soluzione: rendere questi metodi non privati, ma membri di un'altra classe che fornisce funzionalità di raccolta / filtro / tabulazione.

Quindi scrivi i test per gli aspetti stupidi del munging dei dati della classe helper (ad es. "Distinguere i numeri dai caratteri") in un posto e test per il tuo obiettivo principale (ad es. "Ottenere i dati sulle vendite") in un altro posto, e don non è necessario ripetere i test di filtro di base nei test per la normale logica aziendale.

Abbastanza generalmente, se la tua classe che fa una cosa contiene un codice esteso per fare un'altra cosa che è richiesta, ma separata dal suo scopo principale, quel codice dovrebbe vivere in un'altra classe ed essere chiamato con metodi pubblici. Non dovrebbe essere nascosto negli angoli privati ​​di una classe che contiene solo accidentalmente quel codice. Ciò migliora la testabilità e la comprensibilità allo stesso tempo.


Si sono d'accordo con te. Ma ho un problema con la tua prima affermazione, sia la parte "abbastanza complessa" che la parte "abbastanza separata". Per quanto riguarda "abbastanza complesso": sto provando a fare un rapido ciclo rosso-verde, il che significa che posso solo codificare al massimo un minuto circa prima di passare al test (o viceversa). Ciò significa che i miei test saranno davvero molto precisi. Ho pensato che fosse uno dei vantaggi con TDD, ma forse l'ho esagerato, in modo che diventi uno svantaggio.
Henrik Berg,

Per quanto riguarda "abbastanza separato": ho imparato (di nuovo da ziobob) che le funzioni dovrebbero essere piccole e che dovrebbero essere più piccole di così. Quindi, in sostanza, provo a fare 3-4 funzioni di linea. Quindi più o meno tutte le funzionalità sono separate in metodi propri, non importa quanto piccoli e semplici.
Henrik Berg,

Ad ogni modo, mi sento come se gli aspetti di munging dei dati (ad esempio, findNonNumericFields) dovessero essere davvero privati. E se lo separo in un'altra classe, dovrò renderlo pubblico comunque, quindi non vedo esattamente il punto.
Henrik Berg,

6
@HenrikBerg pensa innanzitutto al motivo per cui hai degli oggetti: non sono modi convenienti per raggruppare le funzioni, ma sono unità autonome che semplificano la gestione di sistemi complessi. Quindi, dovresti pensare di testare la classe come una cosa.
gbjbaanb,

@gbjbaanb Direi che sono entrambi uguali.
RubberDuck,

29

Personalmente, penso che tu abbia approfondito la mentalità dell'implementazione quando hai scritto i test. Hai pensato che avresti bisogno di alcuni metodi. Ma hai davvero bisogno che facciano ciò che la classe dovrebbe fare? La classe fallirebbe se qualcuno arrivasse e li rifattorizzasse internamente? Se stavi usando la classe (e questa dovrebbe essere la mentalità del tester secondo me), potresti davvero fregare di meno se esiste un metodo esplicito per verificare la presenza di numeri.

Dovresti testare l'interfaccia pubblica di una classe. L'implementazione privata è privata per un motivo. Non fa parte dell'interfaccia pubblica perché non è necessaria e può cambiare. È un dettaglio di implementazione.

Se scrivi test sull'interfaccia pubblica, non otterrai mai il problema che hai riscontrato. O puoi creare casi di test per l'interfaccia pubblica che coprono i tuoi metodi privati ​​(ottimo) o non puoi. In tal caso, potrebbe essere il momento di riflettere attentamente sui metodi privati ​​e magari eliminarli del tutto se non possono essere raggiunti comunque.


1
"Dettagli di implementazione" sono cose come "ho usato una XOR o una variabile temporanea per scambiare gli inserti tra le variabili". I metodi protetti / privati ​​hanno contratti, come qualsiasi altra cosa. Prendono input, lavorano con esso e producono alcuni output, con determinati vincoli. Alla fine qualsiasi cosa con un contratto dovrebbe essere testata, non necessariamente per coloro che consumano la tua biblioteca, ma per quelli che la mantengono e la modificano dopo di te. Proprio perché non è "pubblico" non significa che non è parte di un'API.
Knetic,

11

Non fai TDD in base a ciò che ti aspetti che la classe faccia internamente.

I tuoi casi di test dovrebbero basarsi su ciò che la classe / funzionalità / programma deve fare al mondo esterno. Nel tuo esempio, l'utente chiamerà mai la tua classe di lettore confind all the non-numerical fields in a line?

Se la risposta è "no", allora è un brutto test scrivere. Volete scrivere il test sulla funzionalità a livello di classe / interfaccia , non il livello "che cosa dovrà implementare il metodo di classe per farlo funzionare", che è il vostro test.

Il flusso di TDD è:

  • rosso (cosa sta facendo la classe / oggetto / funzione / ecc. al mondo esterno)
  • verde (scrivi il codice minimo per far funzionare questa funzione del mondo esterno)
  • refactor (qual è il codice migliore per farlo funzionare)

NON serve "perché in futuro avrò bisogno di X come metodo privato, permettetemi di implementarlo e testarlo prima". Se ti ritrovi a fare questo, stai facendo lo stadio "rosso" in modo errato. Questo sembra essere il tuo problema qui.

Se ti ritrovi spesso a scrivere test per metodi che diventano metodi privati, stai facendo una delle seguenti cose:

  • Non comprendere correttamente la tua interfaccia / i casi d'uso a livello pubblico abbastanza bene da scrivere un test per loro
  • Cambiare drasticamente la progettazione e il refactoring elimina diversi test (il che potrebbe essere una buona cosa, a seconda che questa funzionalità sia testata nei test più recenti)

9

Stai riscontrando un malinteso comune con i test in generale.

La maggior parte delle persone che non conoscono i test iniziano a pensare in questo modo:

  • scrivere un test per la funzione F
  • attuare F
  • scrivere un test per la funzione G
  • implementare G usando una chiamata a F
  • scrivere un test per una funzione H
  • implementare H usando una chiamata a G

e così via.

Il problema qui è che in realtà non hai un unit test per la funzione H. Il test che dovrebbe testare H sta attualmente testando H, G e F allo stesso tempo.

Per risolvere questo, devi capire che le unità testabili non devono mai dipendere l'una dall'altra, ma piuttosto dalle loro interfacce . Nel tuo caso, dove le unità sono semplici funzioni, le interfacce sono solo la loro firma di chiamata. È quindi necessario implementare G in modo tale che possa essere utilizzato con qualsiasi funzione con la stessa firma di F.

Come esattamente ciò possa essere fatto dipende dal tuo linguaggio di programmazione. In molte lingue è possibile passare funzioni (o puntatori ad esse) come argomenti ad altre funzioni. Ciò consentirà di testare ciascuna funzione in modo isolato.


3
Vorrei poter votare più volte. Vorrei solo riassumere come, non hai progettato correttamente la tua soluzione.
Justin Ohms,

In un linguaggio come C, questo ha senso, ma per i linguaggi OO in cui l'unità dovrebbe generalmente essere una classe (con metodi pubblici e privati), allora dovresti testare la classe, non tutti i metodi privati ​​isolati. Isolare le tue lezioni, sì. Isolando i metodi in ogni classe, no.
gbjbaanb,

8

I test che scrivi durante Test Driven Development dovrebbero garantire che una classe implementi correttamente la sua API pubblica, assicurando allo stesso tempo che tale API pubblica sia facile da testare e usare.

Puoi sicuramente utilizzare metodi privati ​​per implementare quell'API, ma non è necessario creare test tramite TDD: la funzionalità verrà testata perché l'API pubblica funzionerà correttamente.

Supponiamo ora che i tuoi metodi privati ​​siano abbastanza complicati da meritare test autonomi, ma non hanno senso come parte dell'API pubblica della tua classe originale. Bene, questo probabilmente significa che dovrebbero effettivamente essere metodi pubblici su qualche altra classe - uno che la tua classe originale sfrutta nella propria implementazione.

Testando solo l'API pubblica, renderai molto più semplice modificare i dettagli di implementazione in futuro. Test inutili ti infastidiranno solo in seguito quando devono essere riscritti per supportare alcuni refactoring eleganti che hai appena scoperto.


4

Penso che la risposta giusta sia la conclusione a cui sei arrivato a partire con i metodi pubblici. Inizierai scrivendo un test che chiama quel metodo. Fallirebbe, quindi crei un metodo con quel nome che non fa nulla. Quindi potresti aver ragione un test che controlla un valore di ritorno.

(Non sono del tutto chiaro su cosa fa la tua funzione. Restituisce una stringa con il contenuto del file con i valori non numerici eliminati?)

Se il tuo metodo restituisce una stringa, controlli quel valore restituito. Quindi continui a costruirlo.

Penso che qualsiasi cosa accada in un metodo privato dovrebbe essere nel metodo pubblico ad un certo punto durante il processo, e quindi spostarsi nel metodo privato solo come parte di una fase di refactoring. Per quanto ne so, il refactoring non richiede test falliti. Hai solo bisogno di test falliti quando aggiungi funzionalità. Hai solo bisogno di eseguire i test dopo il refactor per assicurarsi che tutti passino.


3

mi sento come se mi fossi dipinto in un angolo qui. Ma dove ho fallito esattamente?

C'è un vecchio adagio.

Quando non si pianifica, si prevede di fallire.

La gente sembra pensare che quando TDD ti siedi, scrivi test e il design accadrà magicamente. Questo non è vero. Devi avere un piano di alto livello in corso. Ho scoperto che ottengo i miei migliori risultati da TDD quando progetto prima l'interfaccia (API pubblica). Personalmente, creo un effettivo interfaceche definisce prima la classe.

sussulto Ho scritto un po 'di "codice" prima di scrivere qualsiasi test! Beh no. Non l'ho fatto Ho scritto un contratto da seguire, un design . Ho il sospetto che potresti ottenere risultati simili annotando un diagramma UML su carta millimetrata. Il punto è che devi avere un piano. TDD non è una licenza per hackerare volenti o nolenti un pezzo di codice.

Sento davvero che "Test First" è un termine improprio. Progettare prima quindi testare.

Naturalmente, ti preghiamo di seguire i consigli che altri hanno dato sull'estrazione di più classi dal tuo codice. Se senti fortemente la necessità di testare gli interni di una classe, estrai quegli interni in un'unità facilmente testabile e iniettala.


2

Ricorda che anche i test possono essere sottoposti a refactoring! Se rendi privato un metodo, stai riducendo l'API pubblica, e quindi è perfettamente accettabile eliminare alcuni test corrispondenti per quella "funzionalità perduta" (AKA ha ridotto la complessità).

Altri hanno detto che il tuo metodo privato verrà chiamato come parte degli altri test API, oppure sarà irraggiungibile e quindi cancellabile. In effetti, le cose sono più dettagliate se pensiamo ai percorsi di esecuzione .

Ad esempio, se abbiamo un metodo pubblico che esegue la divisione, potremmo voler testare il percorso che risulta in divisione per zero. Se rendiamo privato il metodo, avremo una scelta: o possiamo considerare il percorso di divisione per zero, oppure possiamo eliminare quel percorso considerando come viene chiamato dagli altri metodi.

In questo modo, potremmo eliminare alcuni test (ad esempio, dividere per zero) e refactoring gli altri in termini di API pubblica rimanente. Certo, in un mondo ideale i test esistenti si occupano di tutti quei percorsi rimanenti, ma la realtà è sempre un compromesso;)


1
Mentre le altre risposte sono corrette in quanto il metodo privato non avrebbe dovuto essere scritto nel ciclo rosso, gli umani commettono errori. E quando hai seguito il percorso dell'errore abbastanza lontano, questa è la soluzione appropriata.
slebetman,

2

Ci sono momenti in cui un metodo privato può diventare un metodo pubblico di un'altra classe.

Ad esempio, potresti avere metodi privati ​​che non sono thread-safe e che lasciano la classe in uno stato temporaneo. Questi metodi possono essere spostati in una classe separata che è detenuta privatamente dalla tua prima classe. Quindi, se la tua classe è una coda, potresti avere una classe InternalQueue che ha metodi pubblici e la classe Queue contiene l'istanza InternalQueue privatamente. Ciò consente di testare la coda interna e chiarisce anche quali sono le singole operazioni su InternalQueue.

(Ciò è più ovvio quando si immagina che non esistesse una classe List e se si è tentato di implementare le funzioni List come metodi privati ​​nella classe che le utilizza.)


2
"Ci sono momenti in cui un metodo privato può diventare un metodo pubblico di un'altra classe." Non posso sottolineare abbastanza. A volte, un metodo privato è semplicemente un'altra classe che urla per la propria identità.

0

Mi chiedo perché la tua lingua abbia solo due livelli di privacy, completamente pubblica e completamente privata.

Puoi organizzare i tuoi metodi non pubblici accessibili al pacchetto o qualcosa del genere? Quindi inserisci i tuoi test nello stesso pacchetto e divertiti a testare i meccanismi interni che non fanno parte dell'interfaccia pubblica. Il sistema di compilazione escluderà i test durante la creazione di un file binario di rilascio.

Ovviamente a volte è necessario disporre di metodi veramente privati, non accessibili a nulla tranne che alla classe di definizione. Spero che tutti questi metodi siano molto piccoli. In generale, mantenere i metodi piccoli (ad es. Sotto le 20 righe) aiuta molto: test, manutenzione e comprensione del codice diventano più facili.


3
La modifica di un modificatore di accesso al metodo solo per eseguire i test è una situazione in cui una coda fa muovere un cane. Penso che testare le parti interne di un'unità stia rendendo più difficile il refactoring in seguito. Il test dell'interfaccia pubblica, al contrario, è ottimo perché funziona come un "contratto" per un'unità.
scriptin,

Non è necessario modificare il livello di accesso di un metodo. Quello che sto cercando di dire è che hai un livello di accesso intermedio che consente di scrivere un certo codice, inclusi i test, più facilmente, senza fare un contratto pubblico. Ovviamente devi testare l'interfaccia pubblica, ma a volte è utile testare alcuni dei meccanismi interni in modo isolato.
9000,

0

Occasionalmente ho superato i metodi privati ​​per proteggermi per consentire test più precisi (più rigorosi dell'API pubblica esposta). Questa dovrebbe essere l'eccezione (si spera molto rara) piuttosto che la regola, ma può essere utile in alcuni casi specifici che potresti incontrare. Inoltre, è qualcosa che non vorresti considerare affatto quando costruisci un'API pubblica, più di "cheat" che puoi usare sul software per uso interno in quelle rare situazioni.


0

L'ho provato e ho sentito il tuo dolore.

La mia soluzione era:

smettere di trattare i test come costruire un monolite.

Ricorda che quando hai scritto una serie di test, diciamo 5, per eliminare alcune funzionalità, non devi tenere tutti questi test in giro , specialmente quando questo diventa parte di qualcos'altro.

Ad esempio ho spesso:

  • test di basso livello 1
  • codice per incontrarlo
  • test di basso livello 2
  • codice per incontrarlo
  • test di basso livello 3
  • codice per incontrarlo
  • test di basso livello 4
  • codice per incontrarlo
  • test di basso livello 5
  • codice per incontrarlo

quindi allora ho

  • test di basso livello 1
  • test di basso livello 2
  • test di basso livello 3
  • test di basso livello 4
  • test di basso livello 5

Tuttavia, se ora aggiungo funzioni di livello superiore che lo chiamano, che hanno molti test, ora potrei essere in grado di ridurre quei test di basso livello per essere solo:

  • test di basso livello 1
  • test di basso livello 5

Il diavolo è nei dettagli e la capacità di farlo dipenderà dalle circostanze.


-2

Il sole gira intorno alla terra o alla terra intorno al sole? Secondo Einstein la risposta è sì, o entrambi poiché entrambi i modelli differiscono solo dal punto di vista, allo stesso modo l'incapsulamento e lo sviluppo guidato dai test sono in conflitto solo perché pensiamo che lo siano. Siamo seduti qui come Galileo e il papa, lanciandoci insulti l'un l'altro: sciocco, non vedi che anche i metodi privati ​​hanno bisogno di essere testati; eretico, non rompere l'incapsulamento! Allo stesso modo quando riconosciamo che la verità è più grande di quanto pensassimo, possiamo provare qualcosa come incapsulare i test per le interfacce private in modo che i test per le interfacce pubbliche non rompano l'incapsulamento.

Prova questo: aggiungi due metodi, uno che non ha input ma che just restituisce il numero di test privati ​​e uno che accetta un numero di test come parametro e restituisce pass / fail.


1
Gli insulti scelti furono usati da Galileo e dal Papa, non da alcuna risposta a questa domanda.
Hildred
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.