Perché i metodi privati ​​di unit test sono considerati cattive pratiche?


16

Contesto:

Attualmente sto lavorando a un piccolo progetto in Python. Strutturo comunemente 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 mucchio di metodi nascosti (che iniziano con il trattino basso) che si occupano del elaborazione complessa o di basso livello.

So che i test sono essenziali per dare fiducia al codice e per garantire che eventuali modifiche successive non abbiano rotto il comportamento precedente.

Problema:

Al fine di 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. Ciò significa che tali test interni possono violare revisioni minori e dovranno essere riparati / sostituiti

Ma so anche che il metodo privato di unit testing è almeno un concetto controverso o più spesso considerato una cattiva pratica. Il motivo è: solo il comportamento pubblico dovrebbe essere testato ( rif. )

Domanda:

Mi interessa seguire le migliori pratiche e vorrei capire:

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

precisioni:

  • non è un modo di mettere in discussione. Python non ha un vero concetto di privacy e i metodi nascosti non sono semplicemente elencati ma possono essere utilizzati quando si conosce il loro nome
  • Non mi sono mai state insegnate regole e schemi di programmazione: le mie ultime lezioni sono degli anni '80 ... Ho imparato principalmente le lingue da prove e fallimenti e riferimenti su Internet (Stack Exchange è il mio preferito da anni)


2
OP, dove hai sentito o letto che la sperimentazione di metodi privati ​​è stata considerata "negativa"? Esistono diversi modi per testare l'unità. Vedi Test unitari, Test black-box e Test white-box .
John Wu,

@JohnWu: conosco la differenza tra i test White-box e Black-box. Ma anche nei test White-box sembra che la necessità di testare metodi privati ​​sia un suggerimento per un problema di progettazione. La mia domanda è un tentativo di capire quali sono i percorsi migliori quando cado lì ...
Serge Ballesta,

2
Ancora una volta, dove hai sentito o letto che anche nei test White-box la necessità di testare metodi privati ​​è un suggerimento per un problema di progettazione? Vorrei capire il ragionamento alla base di tale convinzione prima di tentare una risposta.
John Wu,

@SergeBallesta in altre parole, metti alcuni riferimenti a quegli articoli che ti hanno fatto credere che testare metodi privati ​​sia una cattiva pratica. Quindi spiegaci perché ci hai creduto.
Laiv

Risposte:


17

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 non riutilizzabili, ecc.). C'è quasi sempre qualche problema "più grande" in gioco.

  2. Puoi testarli attraverso l'interfaccia pubblica (che è come li vuoi testare, perché è così che il client li chiamerà / li utilizzerà). Puoi ottenere un falso senso di sicurezza vedendo il via libera su tutti i test di superamento per i tuoi metodi privati. È molto meglio / più sicuro testare casi limite sulle tue funzioni private attraverso la tua interfaccia pubblica.

  3. Rischi una duplicazione grave dei test (test che sembrano / sembrano molto simili) testando metodi privati. Ciò ha conseguenze importanti quando i requisiti cambiano, poiché molti più test del necessario si interromperanno. Può anche metterti in una posizione in cui è difficile eseguire il refactoring a causa della tua suite di test ... che è la massima ironia, perché la suite di test è lì per aiutarti a riprogettare e refactoring in sicurezza!

Un consiglio se sei ancora tentato di testare le parti private (non usarlo se ti dà fastidio e YMMV, ma in passato ha funzionato bene per me): a volte scrivere test unitari per funzioni private solo per essere sicuri stanno funzionando esattamente come pensi che possano essere utili (specialmente se non conosci una lingua). Tuttavia, dopo aver verificato che funzionano, elimina i test e assicurati sempre che test di fronte pubblico siano solidi e cattureranno se qualcuno apporta una modifica significativa a detta funzione privata.

Quando testare metodi privati: poiché questa risposta è diventata (in qualche modo) popolare, mi sento obbligato a dire che una "best practice" è sempre proprio questo: una "best practice". Ciò non significa che dovresti farlo dogmaticamente o alla cieca. Se ritieni di dover testare i tuoi metodi privati ​​e avere un motivo legittimo (come se stessi scrivendo test di caratterizzazione per un'applicazione legacy), quindi testa i tuoi metodi privati . Le circostanze specifiche prevalgono sempre su qualsiasi regola generale o migliore pratica. Basta essere consapevoli di alcune delle cose che possono andare storte (vedi sopra).

Ho una risposta che approfondisce questo argomento in dettaglio su SO che non ripeterò qui: /programming/105007/should-i-test-private-methods-or-only-public-ones/ 47401015 # 47401015


4
Motivo 1: nebuloso. Motivo 2: cosa succede se il metodo dell'helper privato non deve far parte dell'API pubblica? Motivo 3: non se progetti correttamente la tua classe. Il tuo ultimo consiglio: perché dovrei eliminare un test perfettamente valido che dimostra che un metodo che ho scritto funziona?
Robert Harvey,

4
@RobertHarvey Motivo 2: essere indirettamente accessibili tramite l'API pubblica! = Essere parte dell'API pubblica. Se la tua funzione privata non è testabile tramite l'API pubblica, forse è un codice morto e dovrebbe essere rimosso? Oppure la tua classe è davvero un iceberg (motivo 1) e dovrebbe essere riformulata.
Frax,

5
@RobertHarvey se non riesci a testare una funzione privata tramite un'API pubblica, eliminala perché non serve a nulla.
David Arno,

1
@RobertHarvey 1: Gli odori del design sono sempre in qualche modo soggettivi / nebulosi, quindi sicuro. Ma ho elencato alcuni esempi concreti di anti-pattern e ci sono più dettagli nella mia risposta SO. 2: I metodi privati ​​non possono far parte dell'API pubblica (per definizione: sono privati) ... quindi non credo che la tua domanda abbia molto senso. Sto cercando di capire che se hai qualcosa di simile bin_search(arr, item)(pubblico) e bin_search(arr, item, low, hi)(privato, ci sono molti modi per fare la ricerca bin), allora tutto ciò che devi testare è il pubblico di fronte a uno ( bin_search(arr, item))
Matt Messersmith

1
@RobertHarvey 3: In primo luogo, ho detto rischio , non garanzia . In secondo luogo, affermare che "funziona se lo fai correttamente" si autoavvera. Ad esempio, "È possibile scrivere un sistema operativo in una singola funzione se lo si fa correttamente ". Questo non è falso: ma non è utile. Informazioni sul suggerimento: lo elimineresti perché se la tua implementazione cambia (cioè vuoi scambiare un impianto privato), allora la tua suite di test ti ostacolerà (avrai un test fallito dove non dovresti).
Matt Messersmith,

13

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


Non sono sicuro del motivo per cui la tua risposta è stata sottoposta a downgrade. È breve, al punto e ottiene la risposta corretta al 100%.
David Arno,

3
@DavidArno: Forse perché testare metodi privati ​​non ha molto a che fare con la granularità dei test. Ha tutto a che fare con l'accoppiamento ai dettagli di implementazione.
Robert Harvey,

10

La scrittura di unit test per metodi privati collega i test unitari ai dettagli di implementazione.

I test unitari dovrebbero testare il comportamento di una classe sulla superficie esterna della classe (è un'API pubblica). I test unitari non dovrebbero sapere nulla delle viscere di una classe. Scrivere test unitari contro i dettagli di implementazione di una classe ti lega le mani quando arriva il momento di refactoring. Il refactoring quasi sicuramente supererà questi test, perché non fanno parte dell'API stabile.

Detto questo, perché potresti voler scrivere unit test per i tuoi metodi privati?

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

Alcuni ecosistemi come C # e .NET hanno modi per creare ambienti simili a REPL (strumenti come Linqpad lo fanno), ma la loro utilità è limitata perché non si ha accesso al proprio progetto. La finestra immediata in Visual Studio è scomoda; non ha ancora Intellisense completo, devi usare nomi completi e innesca una build ogni volta che lo usi.


4
@ user949300 È un po 'difficile discuterne senza cadere nel vero errore di Scotsman , ma ci sono molti test scritti male di tutti i tipi. Dal punto di vista del test unitario, è necessario testare il contratto pubblico del metodo senza conoscere i dettagli dell'implementazione interna. Non affermare che una certa dipendenza sia stata chiamata X volte è sempre sbagliato: ci sono situazioni in cui ciò ha senso. Devi solo assicurarti che si tratti di un'informazione che desideri effettivamente comunicare nel contratto dell'unità in esame.
Vincent Savard,

3
@DavidArno: [ scrollando le spalle] Lo sto facendo da un po 'di tempo. I test unitari per metodi privati ​​hanno sempre funzionato bene per me, fino a quando Microsoft non ha deciso di interrompere il supporto degli oggetti proxy nel loro framework di test. Di conseguenza, nulla è mai esploso. Non ho mai strappato un buco nell'universo scrivendo un test per un metodo privato.
Robert Harvey,

2
@DavidArno: Perché dovrei smettere di usare una tecnica perfettamente valida che mi offre benefici, solo perché qualcuno su Internet dice che è una cattiva idea senza fornire alcuna giustificazione?
Robert Harvey,

2
Il vantaggio principale che ottengo dai test unitari è quello di darmi una "rete di sicurezza" che mi permetta di armeggiare con il mio codice ed essere sicuro di sapere che le mie modifiche non introducono regressioni. A tal fine, testare metodi di supporto privati ​​rende più facile trovare tali regressioni. Quando refactoring un metodo di supporto privato e introduco un errore logico, interrompo i test specifici per quel metodo privato. Se i miei test unitari fossero più generali e testassero solo l'interfaccia di quell'unità di codice, il problema sarebbe molto più oscuro da trovare.
Alexander - Ripristina Monica il

2
@Frax Certo, potrei, ma secondo quella logica, dovrei rinunciare ai test unitari a favore dei test di integrazione a livello di sistema. Dopotutto, "nella maggior parte dei casi dovresti essere in grado di modificare questi test per testare lo stesso comportamento"
Alexander - Ripristina Monica il

6

Dalla mia esperienza ho scoperto che unità che verifica le classi interne, i metodi di solito significano che devo prendere le funzioni testate, le classi fuori. Per creare un altro livello di astrazione.

Ciò porta a una migliore aderenza al principio della responsabilità unica.


Questa dovrebbe essere la risposta accettata.
Jack Aidley,

0

Penso che questa sia una buona domanda perché espone un problema comune nel testare la copertura. Ma una buona risposta dovrebbe dirvi che la questione non è esattamente giusto, perché, in teoria, si dovrebbe non essere in grado di unit test privati metodi. Ecco perché sono privati .

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

Ricorda che c'è un motivo per cui i tuoi metodi hanno livelli di accessibilità diversi e dovresti sempre pensare a come verranno utilizzate le tue classi alla fine.



Ho cercato di rispondere a questo nella mia domanda dicendo che Python non ha un vero concetto di privacy e che i metodi nascosti non sono semplicemente elencati ma possono essere utilizzati quando si conosce il loro nome
Serge Ballesta,
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.