Perché il test unitario dei metodi privati è considerato una cattiva pratica?

7

Contesto:

Attualmente sto lavorando su un piccolo progetto in Python. Di solito strutturo le mie classi con alcuni metodi pubblici che sono documentati ma riguardano principalmente i concetti di alto livello (ciò che un utente della classe dovrebbe conoscere e usare), e un gruppo di nascosti (a partire da underscore) metodi che sono responsabili dell'elaborazione complessa o di basso livello.

So che i test sono essenziali per dare fiducia al codice e per garantire che qualsiasi modifica successiva non abbia infranto il comportamento precedente.

Problema:

Per costruire metodi pubblici di livello superiore su una base attendibile, generalmente collaudo i metodi privati. Trovo più facile scoprire se una modifica del codice ha introdotto regressioni e dove. Significa che i test interni possono essere violati in piccole revisioni e dovranno essere riparati / sostituiti

Ma so anche che l'unità che testa il metodo privato è almeno un concetto contestato o più spesso considerata una cattiva pratica. Il motivo è: solo il comportamento pubblico dovrebbe essere testato ( ref. )

Domanda:

Mi interessa seguire le best practice e vorrei capire:

  • perché utilizzare i test di unità su metodi privati / nascosti non validi (qual è il rischio)?
  • quali sono le migliori pratiche quando i metodi pubblici possono utilizzare elaborazioni di basso livello e / o complesse?

Precisazioni:

  • non è una domanda come . Python non ha un vero concetto di privacy e i metodi nascosti semplicemente non sono elencati, ma possono essere utilizzati quando si conosce il loro nome
  • Non mi è mai stato insegnato regole e schemi di programmazione: le mie ultime lezioni sono degli anni '80 ... Ho principalmente imparato le lingue per tentativi, errori e riferimenti su Internet (Stack Exchange è il mio preferito da anni)
posta Serge Ballesta 19.10.2018 - 17:20
fonte

5 risposte

10

Un paio di motivi:

  1. In genere quando sei tentato di testare il metodo privato di una classe, è un odore di design (classe iceberg, componenti pubblici riutilizzabili non sufficienti, ecc.). C'è quasi sempre un problema "più grande" in gioco.

  2. Puoi testarli tramite l'interfaccia pubblica (che è il modo in cui vuoi testarli, perché è così che il client li chiamerà / li userà). Puoi avere un falso senso di sicurezza vedendo la luce verde su tutti i test di passaggio per i tuoi metodi privati. È molto meglio / più sicuro testare i casi limite sulle tue funzioni private tramite la tua interfaccia pubblica.

  3. Rischia una duplicazione di test severa (test che sembrano molto simili) testando metodi privati. Questo ha conseguenze importanti quando cambiano i requisiti, poiché molti più test del necessario si interromperanno. Può anche metterti in una posizione in cui è difficile refactoring a causa della tua suite di test ... che è l'ironia finale, perché la suite di test è lì per aiutarti a riprogettazione e refactoring in sicurezza!

Un consiglio se sei ancora tentato di testare le parti private (non usarlo se ti infastidisce, e YMMV, ma ha funzionato bene per me in passato): a volte scrivere test di unità per funzioni private solo per assicurarsi che funzionino esattamente come pensi che possano essere preziosi (specialmente se sei nuovo in una lingua). Tuttavia, dopo esserti assicurato che funzionino, elimina i test e assicurati sempre che i test di confronto pubblici siano solidi e verranno rilevati se qualcuno apporta una modifica eclatante a detta funzione privata.

Quando testare metodi privati: Poiché questa risposta è diventata (abbastanza) popolare, mi sento obbligato a dire che una "best practice" è sempre proprio questa: una "best practice". Ciò non significa che dovresti farlo in modo dogmatico o cieco. Se pensi che dovresti testare i tuoi metodi privati e avere una ragione legittima (come se stessi scrivendo i test di caratterizzazione per un'applicazione legacy), quindi verifica i tuoi metodi privati . Circostanze specifiche sempre vincono qualsiasi regola generale o best practice. Basta essere consapevoli di alcune delle cose che possono andare storte (vedi sopra).

Ho una risposta su questo argomento in dettaglio su SO che non ripeterò qui: link

    
risposta data 19.10.2018 - 22:43
fonte
10

Dato che uno degli scopi principali dei test unitari è che puoi rifattorizzare gli interni del tuo programma e quindi essere in grado di verificare che non hai rotto la sua funzionalità, è controproducente se i tuoi test unitari funzionano a un livello così preciso di granularità che qualsiasi modifica al codice del programma richiede la riscrittura dei test.

    
risposta data 19.10.2018 - 17:52
fonte
9

Scrittura dei test delle unità per i metodi privati lega i test dell'unità ai dettagli dell'implementazione.

I test unitari dovrebbero testare il comportamento di una classe sulla superficie esterna della classe (è una API pubblica). I test unitari non dovrebbero avere bisogno di sapere nulla delle viscere di una classe. I test di unità di scrittura confrontati con i dettagli di implementazione di una classe ti vincolano quando arriva il momento di fare il refactoring. Il refactoring sta quasi certamente andando a infrangere quei test, perché non fanno parte dell'API stabile.

Detto questo, perché potresti vuoi scrivere test unitari per i tuoi metodi privati?

C'è una tensione naturale tra i test unitari e lo sviluppo incrementale. Gli sviluppatori di software che utilizzano un REPL (ciclo read-eval-print) possono attestare quanto possa essere produttivo scrivere rapidamente e testare piccoli bit di funzionalità mentre "crescete" una classe o una funzione. L'unico buon modo per farlo in ambienti che sono unità test-driven è scrivere test unitari per metodi privati, ma c'è molto attrito nel farlo. I test unitari richiedono tempo per scrivere, è necessario un metodo effettivo per testare e il framework di test deve supportare la capacità di mantenere il metodo privato in modo che non inquini l'API esterna.

Alcuni ecosistemi come C # e .NET hanno modi per creare ambienti di tipo REPL (strumenti come Linqpad lo fanno), ma la loro utilità è limitata perché non hai accesso al tuo progetto. La finestra immediata in Visual Studio è scomoda; non ha ancora Intellisense completo, devi usare nomi pienamente qualificati e fa scattare una build ogni volta che la usi.

    
risposta data 19.10.2018 - 17:54
fonte
6

Dalla mia esperienza ho trovato che l'unità testando le classi interne, i metodi di solito significa che devo prendere le funzioni testate, le classi fuori. Per creare un altro livello di astrazione.

Ciò porta a una migliore aderenza al Principio di Responsabilità Unica.

    
risposta data 19.10.2018 - 20:31
fonte
0

Penso che questa sia una buona domanda perché espone un problema comune nella verifica della copertura. Ma una buona risposta dovrebbe dirti che la domanda non è esattamente giusta perché, in teoria, dovresti non essere in grado di testare i metodi privati . Ecco perché sono private .

Forse una domanda migliore sarebbe "Cosa dovrei fare quando voglio testare i metodi privati?" , e la risposta è abbastanza ovvia: dovresti esporli in un modo che rende possibile il test . Ora, questo non significa necessariamente che devi solo rendere pubblico il metodo e basta. Molto probabilmente vorresti fare un'astrazione più alta; passare a una libreria o API diversa in modo da poter eseguire i test su quella libreria, senza esporre tale funzionalità nella tua API principale.

Ricorda che esiste una ragione per cui i tuoi metodi hanno diversi livelli di accessibilità e dovresti sempre pensare a come le tue classi verranno utilizzate alla fine.

    
risposta data 20.10.2018 - 02:49
fonte

Leggi altre domande sui tag