TDD - Fuori dentro contro dentro e fuori


53

Qual è la differenza tra la creazione di un'applicazione Outside In e la costruzione Inside Out utilizzando TDD?

Questi sono i libri che ho letto su TDD e test unitari:
Sviluppo guidato dai test: da esempio
Sviluppo guidato dai test: una guida pratica: una guida pratica
Soluzioni reali per lo sviluppo di framework PHP di alta qualità e applicazioni
Sviluppo guidato dai test in Microsoft. NET
xUnit Test Patterns: Codice di test refactoring
L'arte del test unitario: con esempi in .Net
Software orientato agli oggetti in crescita, guidato da test ---> Questo è stato davvero difficile da capire poiché JAVA non è la mia lingua principale :)

Quasi tutti hanno spiegato le basi del TDD e i test unitari in generale, ma con poca menzione dei diversi modi in cui l'applicazione può essere costruita.

Un'altra cosa che ho notato è che la maggior parte di questi libri (se non tutti) ignorano la fase di progettazione durante la scrittura dell'applicazione. Si concentrano maggiormente sulla scrittura rapida dei casi di test e sulla creazione del design da solo.

Tuttavia, mi sono imbattuto in un paragrafo in xUnit Test Patterns che ha discusso di come le persone si avvicinano al TDD. Ci sono 2 scuole là fuori Outside In vs Inside Out .

Purtroppo il libro non elabora di più su questo punto. Vorrei sapere qual è la differenza principale tra questi 2 casi.
Quando dovrei usare ognuno di essi?
Per un principiante TDD quale è più facile da capire?
Quali sono gli svantaggi di ciascun metodo?
C'è qualche materiale là fuori che discute specificamente di questo argomento?


I due approcci sono descritti nel sito XUnit Test Patterns: xunitpatterns.com/Philosophy%20Of%20Test%20Automation.html . È strano che non siano nel libro.
guillaume31,

Risposte:


45

Inside-Out e Outside-In sono termini abbastanza rari, più spesso ho sentito / letto della scuola classica e della scuola di Londra .

  • Inside-Out (scuola classica, bottom-up ): inizi a livello di componente / classe (interno) e aggiungi test ai requisiti. Man mano che il codice si evolve (a causa di refactoring), compaiono nuovi collaboratori, interazioni e altri componenti. TDD guida completamente il design.

  • Outside-In (scuola di Londra, top-down o "mockist TDD" come lo chiamerebbe Martin Fowler): conosci le interazioni e i collaboratori in anticipo (specialmente quelli ai massimi livelli) e inizi da lì (livello superiore), deridendo le dipendenze necessarie. Con ogni componente finito, si passa ai collaboratori precedentemente derisi e si ricomincia da lì con TDD, creando implementazioni reali (che, sebbene utilizzate, non erano necessarie prima grazie alle astrazioni ). Si noti che l' approccio esterno va bene con il principio YAGNI .

Nessuno degli approcci è l'unico e l'unico ; entrambi hanno il loro posto a seconda di quello che fai. Nelle grandi soluzioni aziendali, in cui parti del design provengono da architetti (o esistono in anticipo) si potrebbe iniziare con un approccio "stile londinese". D'altra parte, quando ti trovi in ​​una situazione in cui non sei sicuro di come dovrebbe apparire il tuo codice (o come dovrebbe adattarsi all'interno di altre parti del tuo sistema), potrebbe essere più facile iniziare con un componente di fascia bassa e lasciarlo evolvono man mano che vengono introdotti più test, refactoring e requisiti.

Qualunque cosa tu usi, il più delle volte è situazionale.

Per ulteriori letture, c'è un post di gruppo di Google con una discussione piuttosto interessante su come questa distinzione (potrebbe aver avuto) origine e perché Londra potrebbe non essere il nome più appropriato.


2
Interessante. Come sei giunto alla conclusione che fuori in TDD c'è TDD "beffardo"? Preferisco di gran lunga il pensiero e il design di Outside-In e quindi i test (vedi softwareonastring.com/2015/01/10/… ), tuttavia l'articolo di Fowler mi colloca saldamente con Fowler nel campo classicista. Mentre il mockist può sempre utilizzare un approccio esterno, non è possibile capovolgerlo e affermare che la progettazione e il collaudo esterni sono mockist TDD. L'esterno può essere e molto è praticato anche dai TDD classicisti.
Marjan Venema,

@jimmy_keen - Con outside-in, sostituisci in qualsiasi momento le simulazioni nei test di livello superiore con le implementazioni effettive create successivamente? Oppure li lasci come dipendenze derise e poi eserciti l'intero codice di produzione come test di integrazione?
thehowler,

1
Non sono d'accordo sul fatto che Classic / Mockist e Inside-Out / Outside-In siano correlati. Sono ortogonali. Puoi usare Inside-Out / Outside-In con entrambi.
Daniel Kaplan,

D'accordo con Daniel. Stai paragonando due tassonomie diverse. Sebbene lo sviluppo di Outside-in sia spesso associato alla scuola londinese (beffarda), non è sempre così.
guillaume31,

Non penso che questa sia una descrizione corretta del processo esterno. Si tratta di testare dalle interfacce pubbliche senza accoppiarsi agli interni, per quanto possibile.
mcintyre321,

15

Risposta breve: come al solito dipenderà dalle preferenze di codifica e dall'approccio del team.

La codifica interna è fantastica perché hai sempre qualcosa che funziona. Il rovescio della medaglia è che non ti aiuta necessariamente ad arrivare in un posto radicalmente diverso. È più difficile tracciare una rotta in questo modo. Allo stesso modo, scrivere il codice al di fuori ha il rovescio della medaglia di non avere necessariamente il vantaggio di un rapido sviluppo iterativo e di non vedere necessariamente tutte le opportunità e i modelli che possono derivare dal profondo della struttura del codice.

Sono arrivato a credere che entrambi gli stili di sviluppo siano importanti e che in effetti sia utile avere un mix di stili in una squadra. L'idea è che dentro e fuori è ottimo per creare blocchi e l'esterno nel pensiero fornisce la struttura e la direzione della forma.

Parte del mio ragionamento proviene da una scuola di pensiero molto popolare che attualmente promuove lo sviluppo iterativo, che è spesso sinonimo di sviluppo al rovescio. Credo che lo sviluppo iterativo sia eccezionale quando non hai troppo lontano da percorrere. Ma penso che il pensiero generale, al contrario di un processo puramente iterativo, abbia un valore inestimabile per alcuni tipi di innovazione e per arrivare a un posto meno ovvio. Una corretta gestione, dentro e fuori insieme, può essere una combinazione molto efficace.


8

Dovresti aggiungere Prinicples Agile, Patterns and Practices in C # a quell'elenco . Non so perché abbia puntato su "in C #" alla fine. I libri non sono affatto la lingua e l'unica ragione per cui non ha ottenuto 5 stelle su Amazon è da persone che sono state deluse dal C # dei suoi esempi.

L'autore sostiene che, quando possibile, dovresti provare a scrivere codice all'esterno e fare affidamento pesantemente sul design evolutivo, e sono d'accordo con la sua affermazione. Il suo ragionamento è che quando aggiungiamo funzionalità, il nostro design si evolverà sempre. Se iniziamo con componenti di basso livello man mano che vengono aggiunte le funzionalità, ci accorgeremo che quei componenti non stanno facendo ciò che vorremmo che facessero o che le cose debbano essere spostate. Ciò potrebbe diventare piuttosto costoso soprattutto se ogni volta che si sposta la funzionalità da una classe all'altra, è necessario eseguire la stessa mossa in tutti i progetti di unit test.

D'altra parte, se si determina in primo luogo cosa dovrebbe fare l'applicazione, si codifica per l'interfaccia esterna. Man mano che le funzionalità vengono aggiunte e il codice in fase di test aumenta di dimensioni, si esegue il refactoring dell'applicazione in più classi, ma mentre questo sforzo di refactoring è in corso, i test unitari originali scritti rimangono validi. Quindi inizi completamente dall'esterno e continui il refactoring in classi sempre più di basso livello, mentre aggiungi ulteriori test unitari a quelle classi interne, ma raramente dovresti spostarti e riscrivere i test unitari.

Tuttavia, se identifichi un sottosistema di basso livello specifico di cui la tua applicazione avrà bisogno (e forse la tua azienda ha già bisogno di tale sottosistema in altre applicazioni), questo sarebbe il momento di iniziare prima con un blocco di basso livello e poi costruisci l'app in aggiunta.


7

A mio modo di vedere, il concetto di sviluppo di Outside-in si estende davvero su 2 livelli. Gerard Meszaros li descrive brevemente come " progettazione esterna" e " codifica esterna / interna / esterna ".

  • Il primo livello è a livello organizzativo e di processo. Il design esterno è inteso al contrario di top-down (cascata / taylorist) e bottom-up. Con un approccio esterno, ci concentriamo sulla prospettiva dell'utente finale. Iniziamo con test di storia, test ATDD o BDD e proseguiamo "verso l'interno" inferendo test tecnici e codice. Quindi il design esterno è in genere quello che faresti in un contesto Agile. Dan North parla molto di approcci BDD, top-down, bottom-up e outside-in.

  • Il secondo livello è tecnico e ha a che fare con strati applicativi. La codifica outside-in significa fondamentalmente partire dall'interfaccia utente e passare all'interno al livello centrale (di solito il livello aziendale / dominio). Si intende in contrapposizione alla codifica Inside-Out, che parte dallo strato centrale e codifica gli strati esterni per ultimi.

Quindi potresti avere un design esterno con la codifica esterna o interna.

Il punto in cui non sono d'accordo con Meszaros è quando associa la codifica Inside-Out con i test di integrazione, sostenendo che in un contesto Inside-Out "in realtà non testiamo il software esterno in isolamento del software interno". Ma credo che nulla ti impedisca di farlo. Puoi scegliere perfettamente di testare gli oggetti del livello esterno deridendo gli oggetti del livello interno anche se il codice di produzione per quelli già esistenti. Devi solo aggiungere interfacce e beffe sopra gli oggetti di cemento esistenti invece di scrivere le interfacce, deridendole e poi creando le implementazioni in un secondo momento, come faresti con la codifica esterna.

In altre parole, il TDD in stile mockist o classicista è l'IMO una preoccupazione ortogonale alla codifica fuori-dentro / dentro-fuori. Puoi usare perfettamente uno stile beffardo insieme a un approccio al rovescio. La ragione di ciò è che lo stile mockist / classicista riguarda le dipendenze del codice mentre la codifica outside-in / inside-out riguarda i livelli applicativi .

Un'altra cosa importante è che le dipendenze non sono solo tra i livelli, ma esistono anche tra oggetti nello stesso livello. Ad esempio, potresti voler iniziare con un oggetto nel tuo livello aziendale centrale (approccio al rovescio) e usare i mock per isolare il tuo oggetto dagli altri oggetti del livello aziendale con cui parla. Ciò accade molto con l'IoC: le astrazioni dalle quali dipende l'oggetto sono spesso dichiarate nello stesso livello, ma le implementazioni concrete sono in un livello diverso.

Robert "Zio Bob" Martin menziona brevemente la codifica al rovescio e come non sia necessariamente in conflitto con un'architettura disaccoppiata nel suo post " Clean Architecture ".

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.