Preferiresti rendere le cose private interne / pubbliche per i test o utilizzare un qualche tipo di hack come PrivateObject?


26

Sono piuttosto un principiante nel test del codice, ed era una assertputtana prima. Una cosa che mi preoccupa nei test unitari è che spesso è necessario che tu faccia public(o almeno internal) campi che privatealtrimenti sarebbero stati , per sbloccarli readonly, invece fare privatemetodi protected virtual, ecc ...

Recentemente ho scoperto che puoi evitarlo usando cose come la classe PrivateObject per accedere a qualsiasi cosa in un oggetto tramite la riflessione. Ma questo rende i tuoi test meno gestibili (le cose falliranno durante l'esecuzione piuttosto che in fase di compilazione, verrà interrotta da una semplice ridenominazione, è più difficile eseguire il debug ...). Qual è la tua opinione su questo ? Quali sono le migliori pratiche nei test unitari riguardanti le restrizioni di accesso?

modifica: considera ad esempio che hai una classe con una cache in un file su disco e nei tuoi test vuoi invece scrivere in memoria.


2
La gente di Python ha solo pubblico. Sono felici. Perché preoccuparsi? Perché non rendere tutto pubblico?
S. Lott,

12
Perché questo è lontano dalle migliori pratiche nella maggior parte delle lingue principali. La vera risposta è usare buone interfacce e così puoi usare oggetti finti per eseguire test.
Rig

2
@Rig: Python non è la miglior lingua? Interessante.
S. Lott,

7
"è ben lungi dall'essere le migliori pratiche nella maggior parte delle lingue principali" non implica (o addirittura allude che logicamente) "Python non è una lingua privilegiata".
Mike Nakis,

1
Precisamente, è una delle migliori lingue ma la maggior parte delle migliori lingue non è Python e sostiene l'impostazione appropriata. Attendo la mia dichiarazione. Esistono modelli progettati per rendere software altamente testabile, garantendo nel contempo che le variabili mantengano l'ambito. Anche i programmatori Python tendono ad emulare l'ambito con i prefissi da quello che ho visto.
Rig

Risposte:


36

Non dovresti mai e poi mai

crea public(o almeno internal) campi che privatealtrimenti sarebbero stati , per sbloccarli readonly, invece fai privatemetodiprotected virtual

Soprattutto il non-lettura di un campo può rendere mutevole un oggetto immutabile, e questo sarebbe un disastro.

I tuoi test dovrebbero considerare i tuoi oggetti come scatole nere ( Wikipedia ), nel senso che dovrebbero riguardare solo l'interfaccia pubblica degli oggetti, non i dettagli della loro implementazione.

Se un oggetto non può essere sufficientemente testato utilizzando la sua interfaccia pubblica, è necessario trovare modi per fornire estensioni formali e utili alla sua interfaccia che facilitino il test. Ad esempio, un sistema di registrazione con solo una coppia di metodi Register / Deregister potrebbe forse beneficiare di un metodo IsRegistered anche se non è necessario per l'applicazione a portata di mano; tuttavia, è un'estensione formale e utile dell'interfaccia e, per inciso, accetterà i test.

L'importante è che cambiare l'implementazione dell'oggetto non dovrebbe richiedere di cambiare il test unitario. In teoria dovresti essere in grado di scrivere il test unitario una volta, quindi chiedere a più programmatori di codificare più implementazioni completamente diverse dell'oggetto da testare per lavorare con il test unitario.


2
Esattamente! Se non riesci a testarlo senza riflessi o hack, probabilmente hai un errore nella tua API o nel tuo design.
Falcon,

Stai dicendo che fare privatemetodi protected virtualper aiutare a deridere nei test è considerata una cattiva pratica?
Robula,

1
@Robula Nel mio libro, sì. Non scrivo nemmeno i miei test nello stesso pacchetto del codice di produzione, perché non ho alcuna utilità per poter accedere a membri non pubblici. Nemmeno io uso le beffe. Ovviamente se devi usare beffe, allora dovrai fare tutto il necessario per facilitare il derisione, quindi il virtuale protetto potrebbe essere un compromesso pratico in molte situazioni. Ma idealmente un design non ha bisogno di simulazioni, solo implementazioni alternative, e idealmente i test sono test in scatola nera, non test in scatola bianca, quindi le cose vengono testate in integrazione, senza beffe.
Mike Nakis,

13

In C # è possibile utilizzare InternalsVisibleToAttributeper consentire all'assembly di test di vedere le internalclassi nell'assembly che si sta testando. Sembra che tu lo sappia già.

Nella maggior parte dei casi sono interessato solo a testare l'API pubblica del mio assembly. In un caso in cui voglio fare un test in white box di alcune unità, sposto il codice in una internalclasse e lo trasformo in un publicmetodo di quella classe. Quindi posso codificare un test unitario per quella classe.

Questo si presta bene all'iniezione di dipendenza e al modello di strategia. Puoi astrarre il metodo in un'interfaccia, iniettare l'interfaccia nel costruttore del tuo oggetto genitore e testare che il genitore deleghi in modo appropriato. Quindi è possibile verificare che l'oggetto figlio si comporti in modo appropriato. Questo rende tutto più modulare.


8

Nella mia esperienza, è meglio non esporre gli interni della tua classe appositamente per il test. Anche se questo può sembrare per facilitare la scrittura del test. Il test è il primo client della tua classe, quindi se è difficile testare la tua classe attraverso la sua interfaccia pubblica, probabilmente sarà difficile usare la tua classe anche in altri posti.

Quanto è facile testare la classe attraverso la sua API pubblica è un feedback su quanto sarà facile usare la classe. Pensa ai test parzialmente come un modo per prototipare l'uso della tua classe.

Prova a considerare perché il tuo test deve rilevare qualcosa sulla classe che non è disponibile tramite l'API pubblica. Forse la tua classe sta facendo troppo e deve invece essere scomposta in un gruppo di classi cooperanti? Quindi puoi prendere in giro alcune delle classi collaboranti per capire cosa sta facendo la classe sottoposta a test.

Prova ad applicare il principio di inversione di dipendenza e introduce interfacce tra le tue classi che puoi prendere in giro nei tuoi test. È possibile fornire i collaboratori al test utilizzando l' inversione del controllo .

Considera anche l'applicazione del principio "non chiedere" per aiutare a strutturare la tua interfaccia di classe in un modo robusto, liberamente accoppiato, in cui sono esposte le giuste informazioni e il resto nascosto.


6

Personalmente preferisco lasciare il codice che sto testando il più puro possibile e se il codice di test ha bisogno di hack (e in c ++ cattivi puntatori ecc.) Sono felice di farlo.



1
Sono d'accordo, questo è il modo di farlo. Mantieni la bruttezza nel codice di prova, dove non può danneggiare nulla.
Alan Delimon,

@AlanDelimon Potrebbe non danneggiare la mente dei programmatori di manutenzione successivi?
flamingpenguin,

1
Il brutto codice di @flamingpenguin può danneggiare i programmatori ovunque; ma il brutto codice di prova probabilmente farà meno danni poiché influenzerà solo le persone che modificano la classe e non anche le persone che semplicemente lo consumano.
Dan Neely,

2
@flamingpenguin Potrebbe, ma se devi mettere un codice brutto da qualche parte, potrebbe anche essere in un test unitario in cui è meno probabile che necessiti di modifiche ed essere parte dell'applicazione.
Alan Delimon,

5

La visibilità del tuo codice non ha nulla a che fare con i tuoi test unitari!

Rendi i tuoi metodi privati ​​non allo scopo di nasconderli dai test e non li rendi pubblici da esporre per i test, giusto? Questa è la tua implementazione che è essenziale qui, non i test - quei server come prova del tuo codice, ma non sono il tuo codice .

Esistono molti modi per consentire l'accesso a metodi e proprietà nascosti (privati ​​o interni). Molti framework di test e IDE (ad esempio Visual Studio) ti supportano qui generando tutto il necessario per l'accesso, devi solo creare i tuoi casi di test. Quindi, perché preoccuparsi? Meglio mantenere il codice pulito e ordinato.

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.