Ho un'implementazione scheletrica, come nell'articolo 18 di Effective Java (discussione estesa qui ). È una classe astratta che fornisce 2 metodi pubblici methodA () e methodB () che chiamano i metodi delle sottoclassi per "colmare le lacune" che non posso definire in modo astratto.
L'ho sviluppato prima creando una classe concreta e scrivendone dei test unitari. Quando è arrivata la seconda classe, sono stato in grado di estrarre il comportamento comune, implementare gli "spazi mancanti" nella seconda classe ed era pronto (ovviamente, i test unitari sono stati creati per la seconda sottoclasse).
Il tempo è passato e ora ho 4 sottoclassi, ognuna delle quali implementa 3 metodi protetti brevi che sono molto specifici per la loro implementazione concreta, mentre l'implementazione scheletrica fa tutto il lavoro generico.
Il mio problema è che quando creo una nuova implementazione, scrivo di nuovo i test:
- La sottoclasse chiama un determinato metodo richiesto?
- Un determinato metodo ha una determinata annotazione?
- Ottengo i risultati previsti dal metodo A?
- Ottengo i risultati previsti dal metodo B?
Mentre vedi i vantaggi con questo approccio:
- Documenta i requisiti della sottoclasse attraverso i test
- Potrebbe fallire rapidamente in caso di refactoring problematico
- Testa la sottoclasse nel suo insieme e attraverso la loro API pubblica ("funziona methodA ()?" E non "funziona questo metodo protetto?")
Il problema che ho è che i test per una nuova sottoclasse sono fondamentalmente un gioco da ragazzi: - I test sono tutti uguali in tutte le sottoclassi, cambiano semplicemente il tipo di ritorno dei metodi (l'implementazione dello scheletro è generica) e le proprietà I controllare la parte affermativa del test. - Di solito le sottoclassi non hanno test specifici per loro
Mi piace il modo in cui i test si concentrano sul risultato della sottoclasse stessa e la proteggono dal refactoring, ma il fatto che l'implementazione del test sia diventato solo un "lavoro manuale" mi fa pensare che sto facendo qualcosa di sbagliato.
Questo problema è comune durante il test della gerarchia di classi? Come posso evitarlo?
ps: ho pensato di testare la classe skeleton, ma sembra anche strano creare una simulazione di una classe astratta per poterla testare. E penso che qualsiasi refactoring sulla classe astratta che cambia il comportamento che non è previsto dalla sottoclasse non sarebbe notato così velocemente. Le mie viscere mi dicono che testare la sottoclasse "nel suo insieme" è l'approccio preferito qui, ma non esitate a dirmi che mi sbaglio :)
ps2: ho cercato su Google e ho trovato molte domande sull'argomento. Uno di questi è questo che ha una grande risposta da Nigel Thorne, il mio caso sarebbe il suo "numero 1". Sarebbe fantastico, ma non posso refactoring a questo punto, quindi dovrei convivere con questo problema. Se potessi fare il refactoring, avrei ciascuna sottoclasse come strategia. Sarebbe OK, ma testerei ancora la "classe principale" e testerei le strategie, ma non noterei alcun refactoring che interrompe l'integrazione tra classe principale e strategie.
ps3: ho trovato alcune risposte dicendo che "è accettabile" testare la classe astratta. Sono d'accordo che questo è accettabile, ma vorrei sapere qual è l'approccio preferito in questo caso (sto ancora iniziando con i test unitari)