Un flusso di lavoro BDD con Behat e Phpecec

In questo tutorial, daremo un'occhiata a due diversi strumenti BDD, Behat e phpspec, e vediamo come possono supportarti nel tuo processo di sviluppo. L'apprendimento di BDD può essere fonte di confusione. Nuova metodologia, nuovi strumenti e molte domande, come "cosa testare?" e "quali strumenti usare?". Spero che questo esempio piuttosto semplice ti fornisca idee su come integrare BDD nel tuo flusso di lavoro.

La mia ispirazione

Mi sono ispirato a scrivere questo tutorial di Taylor Otwell, creatore del framework Laravel. Diverse volte, ho sentito Taylor spiegare perché per lo più non fa TDD / BDD dicendo che gli piace prima pianificare l'API del suo codice, prima di iniziare effettivamente a implementarlo. L'ho sentito da molti sviluppatori e ogni volta penso a me stesso: "Ma questo è il caso d'uso perfetto per TDD / BDD!". Taylor dice che gli piace mappare l'API del suo codice, scrivendo il codice che desidera avere. Avvierà quindi la codifica e non sarà soddisfatto finché non avrà raggiunto quell'API esatta. L'argomento ha senso se stai solo testando / speculando a livello di unità, ma usando uno strumento come Behat, inizi con il comportamento esterno del tuo software, che è fondamentalmente, per quanto ho capito, che cosa Taylor vuole realizzare.

Cosa copriremo

In questo tutorial, costruiremo una semplice classe caricatore di file di configurazione. Inizieremo utilizzando l'approccio di Taylor e poi adotteremo un approccio BDD. Gli esempi sono minimalisti, ma dovremo preoccuparci di fixture, metodi statici ecc. Quindi, nel complesso, penso che dovrebbero essere sufficienti per mostrare come Behat e phpepec possano completarsi a vicenda.

Disclaimer: Prima di tutto, questo articolo non è un iniziare guida. Presuppone le conoscenze di base di BDD, Behat e phpecec. Probabilmente hai già esaminato questi strumenti, ma stai ancora cercando di utilizzarli nel tuo flusso di lavoro quotidiano. Se vuoi rispolverare su phpspec, dai un'occhiata al mio tutorial introduttivo. In secondo luogo, sto usando Taylor Otwell come esempio. Non so nulla su come funziona Taylor, oltre a ciò che ho sentito dire nei podcast, ecc. Lo uso come esempio perché è uno sviluppatore eccezionale (ha creato Laravel!) E perché è famoso. Potrei anche aver usato qualcun altro, dato che la maggior parte degli sviluppatori, incluso me stesso, non fanno il BDD tutto il tempo, ancora. Inoltre, non sto dicendo che il flusso di lavoro che Taylor descrive sia cattivo. Penso che sia una brillante idea mettere un po 'di pensiero nel tuo codice prima di scriverlo davvero. Questo tutorial ha lo scopo di mostrare il modo BDD di farlo.

Taylor's Workflow

Cominciamo col dare un'occhiata a come Taylor potrebbe andare a progettare questo caricatore di file di configurazione. Taylor dice che gli piace solo aprire un file di testo vuoto nel suo editor e poi scrivere come vorrebbe che gli sviluppatori fossero in grado di interagire con il suo codice (l'API). In un contesto BDD, questo è normalmente definito come test del comportamento esterno del software e strumenti come Behat sono grandi per questo. Lo vedremo fra poco.

In primo luogo, forse Taylor prenderà una decisione sui file di configurazione. Come dovrebbero funzionare? Come in Laravel, usiamo solo matrici PHP semplici. Un esempio di file di configurazione potrebbe assomigliare a questo:

# config.php  'UTC',);

Quindi, come dovrebbe funzionare il codice che utilizza questo file di configurazione? Facciamo in modo Taylor e scriviamo il codice che vorremmo avere:

$ config = Config :: load ('config.php'); $ Config-> get ( 'fuso orario'); // restituisce 'UTC' $ config-> get ('timezone', 'CET'); // restituisce 'CET' se 'fuso orario' non è configurato $ config-> set ('timezone', 'GMT'); $ Config-> get ( 'fuso orario'); // restituisce 'GMT' 

Ok, quindi sembra abbastanza buono. Per prima cosa abbiamo una chiamata statica a a caricare() funzione, seguita da tre casi d'uso: 

  1. Ottenere il "fuso orario" dal file di configurazione. 
  2. Ottenere un valore predefinito, se il "fuso orario" non è ancora configurato. 
  3. Modifica di un'opzione di configurazione da ambientazione a qualcos'altro. Descriveremo ciascuno di questi casi d'uso, o scenari, con Behat tra poco.

Ha senso usare Behat per questo genere di cose. Behat non ci costringerà a prendere decisioni sul design - abbiamo phpepec per questo. Sposteremo semplicemente i requisiti, che abbiamo appena descritto, in un Behat caratteristica per assicurarci di aver capito bene, quando iniziamo a costruire. La nostra funzione Behat servirà da test di accettazione per le nostre esigenze, per così dire.

Se si guarda il codice che abbiamo scritto, un altro motivo per usare Behat, invece di solo phpspec, è la chiamata statica. Non è facile testare metodi statici, specialmente se non si sta usando uno strumento come phpspec. Vedremo come possiamo fare questo quando avremo sia Behat che phpspec disponibili.

Impostare

Supponendo che stai utilizzando Composer, la configurazione di Behat e phspec è un processo in due fasi molto semplice.

In primo luogo, hai bisogno di un base composer.json file. Questo include Behat e phpepec e utilizza psr-4 per caricare automaticamente le classi:

"require-dev": "behat / behat": "~ 3.0", "phpspec / phpspec": "~ 2.0", "autoload": "psr-4": "": "src /"  

Correre installazione di compositore per recuperare le dipendenze.

phpspec non ha bisogno di alcuna configurazione per funzionare, mentre Behat ha bisogno che tu esegua il seguente comando per generare uno scaffold di base:

$ vendor / bin / behat --init

Questo è tutto ciò che serve. Questo non può essere la tua scusa per non fare BDD!

Pianificare le funzionalità con Behat

Quindi, ora che siamo tutti pronti, siamo pronti per iniziare a fare BDD. Perché fare BDD significa installare e usare Behat e phpec, giusto?

Per quanto mi riguarda, abbiamo già iniziato a fare BDD. Abbiamo efficacemente descritto il comportamento esterno del nostro software. I nostri "clienti" in questo esempio sono sviluppatori che interagiranno con il nostro codice. Con "efficacemente", intendo che abbiamo descritto il comportamento in un modo che capiranno. Potremmo prendere il codice che abbiamo già delineato, inserirlo in un file README e ogni sviluppatore PHP decente potrebbe capire come utilizzarlo. Quindi questo è abbastanza buono in realtà, ma ho due cose importanti da notare su questo. Innanzitutto, la descrizione del comportamento del software che utilizza il codice funziona solo in questo esempio perché i "client" sono programmatori. Normalmente, stiamo testando qualcosa che verrà utilizzato da persone "normali". Un linguaggio umano è migliore di PHP quando vogliamo comunicare con gli umani. In secondo luogo, perché non automatizzare questo? Non ho intenzione di discutere perché questa potrebbe essere una buona idea.

Detto questo, penso che iniziare a usare Behat ora sarebbe una decisione ragionevole.

Usando Behat, desideriamo descrivere ciascuno degli scenari che abbiamo delineato sopra. Non vogliamo coprire estesamente tutti i casi limite coinvolti nell'utilizzo del software. Abbiamo phpspec disponibile se questo dovrebbe essere necessario per correggere bug lungo la strada, ecc. Penso che molti sviluppatori, forse incluso Taylor, sentano come se dovessero pensare a tutto e decidere tutto prima di poter scrivere test e specifiche. Ecco perché scelgono di iniziare senza BDD, perché non vogliono decidere tutto in anticipo. Questo non è il caso di Behat, dal momento che stiamo descrivendo il comportamento e l'utilizzo esterni. Per utilizzare Behat per descrivere una funzionalità, non è necessario decidere nulla di più rispetto all'esempio precedente con l'utilizzo di un file di testo non elaborato. Abbiamo solo bisogno di definire i requisiti della funzione - in questo caso l'API esterna della classe del caricatore dei file di configurazione.

Ora, prendiamo il codice PHP sopra riportato e lo trasformiamo in una funzione Behat, usando la lingua inglese (in realtà usando la lingua Gherkin).

Crea un file in Caratteristiche/ directory, chiamato config.feature, e compilare i seguenti scenari:

Caratteristica: file di configurazione Per configurare la mia applicazione Come sviluppatore devo essere in grado di memorizzare le opzioni di configurazione in un file Scenario: ottenere un'opzione configurata Dato che c'è un file di configurazione E l'opzione 'fuso orario' è configurata su 'UTC' Quando Carico il file di configurazione Quindi dovrei ottenere 'UTC' come opzione 'fuso orario' Scenario: Ottenere un'opzione non configurata con un valore predefinito Dato che c'è un file di configurazione E l'opzione 'fuso orario' non è ancora configurata Quando carico la configurazione file Quindi dovrei ottenere il valore predefinito 'CET' come opzione 'timezone' Scenario: impostare un'opzione di configurazione Dato che c'è un file di configurazione E l'opzione 'timezone' è configurata su 'UTC' Quando carico il file di configurazione E imposto il ' 'opzione di configurazione del fuso orario' GMT 'Quindi dovrei ottenere' GMT 'come opzione' fuso orario ' 

In questa funzione, stiamo descrivendo, dall'esterno, come "uno sviluppatore" sarebbe in grado di memorizzare le opzioni di configurazione. Non ci interessa il comportamento interno: lo faremo quando inizieremo a utilizzare phpspec. Finché questa funzione diventa verde, non ci importa cosa succede dietro le quinte.

Lascia che eseguiamo Behat e vediamo cosa pensa della nostra funzione:

$ vendor / bin / behat --dry-run --append-snippets 

Con questo comando, diciamo a Behat di aggiungere le definizioni di passaggi necessarie al nostro contesto di funzionalità. Non entrerò molto nei dettagli su Behat, ma questo aggiunge un mucchio di metodi vuoti ai nostri FeatureContext classe che esegue il mapping ai nostri passaggi di funzionalità sopra.

Ad esempio, dai un'occhiata alla definizione del passo che Behat ha aggiunto per c'è un file di configurazione passo che usiamo come passo "dato" in tutti e tre gli scenari:

/ ** * @Given c'è un file di configurazione * / public function thereIsAConfigurationFile () throw new PendingException ();  

Ora, tutto ciò che dobbiamo fare è compilare un codice per descriverlo.

Scrivere le definizioni dei passaggi

Prima di iniziare, ho due punti importanti da fare riguardo alle definizioni dei passi:

  1. Il punto è dimostrare che il comportamento ora, non è come vogliamo che sia. Dopodiché, possiamo iniziare a progettare il nostro codice, la maggior parte delle volte usando phpepec, per arrivare al verde.
  2. L'implementazione delle definizioni di step non è importante - abbiamo solo bisogno di qualcosa che funzioni e raggiunga "1". Possiamo refactoring più tardi.

Se corri vendor / bin / Behat, vedrai che tutti gli scenari ora hanno passaggi in sospeso.

Iniziamo con il primo passo Dato che c'è un file di configurazione. Useremo un fixture del file di configurazione, quindi possiamo usare lo statico caricare() metodo più tardi. Ci preoccupiamo per l'elettricità statica caricare() metodo perché ci dà una bella API Config :: load (), molto simile alle facciate di Laravel. Questo passaggio potrebbe essere implementato in numerosi modi. Per ora, penso che dovremmo assicurarci di avere la fixture disponibile e che contenga un array:

/ ** * @Given c'è un file di configurazione * / public function thereIsAConfigurationFile () if (! File_exists ('fixtures / config.php')) lancia una nuova Exception ("File 'fixtures / config.php' non trovata") ; $ config = include 'fixtures / config.php'; se (! is_array ($ config)) lancia una nuova eccezione ("File 'fixtures / config.php' dovrebbe contenere un array");  

Stiamo per arrivare al verde con questo passaggio senza implementare alcun codice oltre a rendere il dispositivo. Lo scopo di a Dato passo è mettere il sistema in uno stato conosciuto. In questo caso, ciò significa assicurarsi di avere un file di configurazione.

Per arrivare al nostro primo passo verde, abbiamo solo bisogno di creare il dispositivo:

# fixtures / config.php 

Successivamente, abbiamo un E passo, che in questo caso è solo un alias per Dato. Vogliamo assicurarci che il file di configurazione contenga un'opzione per il fuso orario. Di nuovo, questo è solo collegato al nostro dispositivo, quindi non ci interessa molto. Ho premuto il seguente codice (hackish) per realizzare questo:

/ ** * @Vedi l'opzione: l'opzione è configurata su: valore * / funzione pubblica theOptionIsConfiguredTo ($ option, $ value) $ config = include 'fixtures / config.php'; if (! is_array ($ config)) $ config = []; $ config [$ option] = $ valore; $ content = "

Il codice sopra non è bello, ma realizza ciò di cui ha bisogno. Ci consente di manipolare il nostro dispositivo all'interno della nostra funzione. Se esegui Behat, vedrai che ha aggiunto l'opzione "timezone" al config.php apparecchio:

 'UTC',); 

Ora è il momento di introdurre parte del "codice Taylor" originale! Il gradino Quando carico il file di configurazione consisterà in un codice che ci interessa davvero. Inseriremo parte del codice dal file di testo non elaborato in precedenza e assicuriamoci che venga eseguito:

/ ** * @Quando carico il file di configurazione * / public function iLoadTheConfigurationFile () $ this-> config = Config :: load ('fixtures / config.php'); // Taylor! 

Eseguendo Behat, naturalmente questo fallirà, da allora config non esiste ancora. Portiamo phpspec in soccorso!

Progettare con Phpecec

Quando eseguiamo Behat, avremo un errore fatale:

PHP Errore irreversibile: classe "Config" non trovata ... 

Per fortuna, abbiamo phpspec disponibile, inclusi i suoi generosi generatori di codice:

$ vendor / bin / phpspec desc "Config" Specifica per Config creata in ... /spec/ConfigSpec.php. $ vendor / bin / phpspec run --format = pretty Vuoi creare 'Config' per te? y $ vendor / bin / phpspec run --format = pretty Config 10 ✔ è inizializzabile 1 specifiche 1 esempio (1 superato) 7ms 

Con questi comandi, phpspec ha creato per noi i seguenti due file:

spec / '- ConfigSpec.php src /' - Config.php

Questo ci ha sbarazzato del primo errore fatale, ma Behat non è ancora in esecuzione:

Errore fatale PHP: chiamata al metodo non definito Config :: load () in ... 

caricare() sta per essere un metodo statico e in quanto tale, non è facilmente reperibile con phpspec. Per due motivi questo è OK, però:

  1. Il comportamento del caricare() il metodo sarà molto semplice. Se in seguito avremo bisogno di più complessità, possiamo estrarre la logica in piccoli metodi testabili.
  2. Il comportamento, per ora, è coperto abbastanza bene da Behat. Se il metodo non carica correttamente il file in un array, Behat verrà sballottato da noi.

Questa è una di quelle situazioni in cui molti sviluppatori colpiranno il muro. Lanciano phpspec e concludono che fa schifo e sta lavorando contro di loro. Ma, guarda come Behat e phpepec si completano a vicenda?

Invece di cercare di ottenere una copertura del 100% con phpspec, applichiamo semplicemente un semplice caricare() funzione e sii sicuro che è coperto da Behat:

settings = array ();  carico statico pubblico ($ path) $ config = new static (); if (file_exists ($ path)) $ config-> settings = include $ path; restituire $ config;  

Siamo abbastanza fiduciosi che le nostre opzioni di configurazione sono ora caricate. Altrimenti, il resto dei nostri passi fallirà e potremo esaminarlo nuovamente.

Costruire la feature con Iteration

Tornando al verde con Behat e phpspec, ora possiamo guardare al nostro prossimo passo Quindi dovrei ottenere l'opzione 'UTC' come opzione 'fuso orario'.

/ ** * @Quando dovrei ottenere: valore come: opzione opzione * / funzione pubblica iShouldGetAsOption (valore $, opzione $) $ actual = $ this-> config-> get ($ option); // Taylor! if (! strcmp ($ value, $ actual) == 0) lancia una nuova Exception ("Previsto $ actual da essere '$ option'.");  

In questo passo scriviamo più di quel codice che desideriamo di avere. Correre Behat però, vedremo che non abbiamo un ottenere() metodo disponibile:

Errore fatale PHP: chiamata al metodo non definito Config :: get () in ... 

È tempo di tornare su phpepec e risolvere questo problema.

Testare accessori e mutatori, getter e setter dell'AKA, è quasi come quel vecchio dillemma di pollo o uovo. Come possiamo testare il ottenere() metodo se non abbiamo ancora il impostato() metodo e viceversa. Il modo in cui tendo a fare questo è testarli entrambi contemporaneamente. Ciò significa che in realtà implementeremo la funzionalità per impostare un'opzione di configurazione, anche se non abbiamo ancora raggiunto tale scenario.

Il seguente esempio dovrebbe fare:

function it_gets_and_sets_a_configuration_option () $ this-> get ('foo') -> shouldReturn (null); $ this-> set ('foo', 'bar'); $ This-> get ( 'foo') -> shouldReturn ( 'bar');  

Innanzitutto, avremo i generatori phpspec che ci aiutano a iniziare:

$ vendor / bin / phpspec run --format = pretty Vuoi creare 'Config :: get ()' per te? y $ vendor / bin / phpspec run --format = pretty Vuoi creare 'Config :: set ()' per te? y $ vendor / bin / phpspec run --format = pretty Config 10 ✔ è inizializzabile 15 ✘ ottiene e imposta un'opzione di configurazione prevista "bar", ma ha ottenuto null ... 1 specifiche 2 esempi (1 superato, 1 non riuscito) 9ms 

Ora, torniamo al verde:

funzione pubblica get ($ option) if (! isset ($ this-> settings [$ option])) restituisce null; restituire $ this-> settings [$ option];  set di funzioni pubbliche ($ option, $ value) $ this-> settings [$ option] = $ value;  

E, eccoci:

$ vendor / bin / phpspec eseguito --format = pretty Config 10 ✔ è inizializzabile 15 ✔ ottiene e imposta un'opzione di configurazione 1 specifiche 2 esempi (2 passati) 9ms 

Questo ci ha fatto molta strada. Eseguendo Behat, vediamo che ora siamo nel secondo scenario. Successivamente, dobbiamo implementare la funzione di opzione predefinita, dal momento che ottenere() sta solo tornando nullo proprio adesso.

Il primo passaggio di funzionalità è simile a quello che abbiamo scritto in precedenza. Invece di aggiungere l'opzione all'array, lo faremo unset esso:

/ ** * @Vai l'opzione: l'opzione non è ancora configurata * / public function theOptionIsNotYetConfigured ($ option) $ config = include 'fixtures / config.php'; if (! is_array ($ config)) $ config = []; unset ($ config [$ opzione]); $ content = "

Questo non è carino Lo so! Potremmo sicuramente refactoring, dal momento che stiamo ripetendo noi stessi, ma questo non è lo scopo di questo tutorial.

Anche il secondo passaggio di funzionalità sembra familiare ed è principalmente copia e incolla di prima:

/ ** * @Quando dovrei ottenere il valore predefinito: default come: opzione opzione * / funzione pubblica iShouldGetDefaultValueAsOption ($ default, $ opzione) $ actual = $ this-> config-> get ($ option, $ default); // Taylor! if (! strcmp ($ default, $ actual) == 0) lancia una nuova Exception ("Previsto $ actual da essere '$ default'.");  

ottenere() sta tornando nullo. Passiamo a phpepec e scriviamo un esempio per risolvere questo:

function it_gets_a_default_value_when_option_is_not_set () $ this-> get ('foo', 'bar') -> shouldReturn ('bar'); $ this-> set ('foo', 'baz'); $ this-> get ('foo', 'bar') -> shouldReturn ('baz');  

Per prima cosa, controlliamo che otteniamo il valore predefinito se "opzione" non è ancora stata configurata. In secondo luogo, ci assicuriamo che l'opzione predefinita non sovrascriva un'opzione configurata.

A prima vista, phpspec potrebbe sembrare eccessivo in questo caso, dal momento che stiamo già testando la stessa cosa con Behat. Mi piace usare phpspec per specificare i casi limite, che sono in qualche modo impliciti nello scenario. E inoltre, i generatori di codice di phpec sono davvero grandiosi. Li uso per tutto e mi ritrovo a lavorare più velocemente ogni volta che utilizzo phpspec.

Ora, phpspec conferma ciò che Behat ci ha già detto:

$ vendor / bin / phpspec eseguire --format = pretty Config 10 ✔ è inizializzabile 15 ✔ ottiene e imposta un'opzione di configurazione 24 ✘ ottiene un valore predefinito quando l'opzione non è impostata prevista "bar", ma ha ottenuto null ... 1 specifiche 3 esempi ( 2 passati, 1 fallito) 9ms 

Per tornare al verde, aggiungeremo un "ritorno anticipato" al ottenere() metodo:

funzione pubblica get ($ option, $ defaultValue = null) if (! isset ($ this-> settings [$ option]) e! is_null ($ defaultValue)) return $ defaultValue; se (! isset ($ this-> settings [$ option])) restituisce null; restituire $ this-> settings [$ option];  

Vediamo che phpspec ora è felice:

$ vendor / bin / phpspec eseguire --format = pretty Config 10 ✔ è inizializzabile 15 ✔ ottiene e imposta un'opzione di configurazione 24 ✔ ottiene un valore predefinito quando l'opzione non è impostata 1 specifiche 3 esempi (3 passati) 9ms 

E così è Behat, e anche noi.

Abbiamo finito con il nostro secondo scenario e ne abbiamo lasciato uno. Per l'ultimo scenario, abbiamo solo bisogno di scrivere la definizione del passo per il E ho impostato l'opzione di configurazione "timezone" su "GMT" passo:

/ ** * @Quando imposto l'opzione: opzione di configurazione su: valore * / funzione pubblica iSetTheConfigurationOptionTo ($ option, $ value) $ this-> config-> set ($ option, $ value); // Taylor!  

Dal momento che abbiamo già implementato il impostato() metodo, questo passaggio è già verde:

$ vendor / bin / behat Caratteristica: file di configurazione Per configurare la mia applicazione Come sviluppatore devo essere in grado di memorizzare le opzioni di configurazione in un file Scenario: ottenere un'opzione configurata # features / config.feature: 6 Dato che c'è una configurazione file # FeatureContext :: thereIsAConfigurationFile () E l'opzione 'timezone' è configurata su 'UTC' # FeatureContext :: theOptionIsConfiguredTo () Quando carico il file di configurazione # FeatureContext :: iLoadTheConfigurationFile () Allora dovrei ottenere 'UTC' come 'fuso orario' 'opzione # FeatureContext :: iShouldGetAsOption () Scenario: ottenere un'opzione non configurata con un valore predefinito # features / config.feature: 12 Dato che c'è un file di configurazione # FeatureContext :: thereIsAConfigurationFile () E l'opzione' timezone 'non è ancora configurato # FeatureContext :: theOptionIsNotYetConfigured () Quando carico il file di configurazione # FeatureContext :: iLoadTheConfigurationFile () Quindi dovrei ottenere il valore predefinito 'CET' come opzione 'timezone' # FeatureContext :: iShould Scenario GetDefaultValueAsOption (): Impostazione di un'opzione di configurazione # features / config.feature: 18 Dato che esiste un file di configurazione # FeatureContext :: thereIsAConfigurationFile () E l'opzione 'timezone' è configurata su 'UTC' # FeatureContext :: theOptionIsConfiguredTo () When Carico il file di configurazione # FeatureContext :: iLoadTheConfigurationFile () E ho impostato l'opzione di configurazione 'timezone' su 'GMT' # FeatureContext :: iSetTheConfigurationOptionTo () Allora dovrei ottenere 'GMT' come opzione 'fuso orario' # FeatureContext :: iShouldGetAsOption ( ) 3 scenari (3 passati) 13 passaggi (13 passati) 0m0.04s (8,92Mb) 

Incartare

Tutto è bello e verde, quindi facciamo un rapido riassunto e vediamo cosa abbiamo realizzato.

Abbiamo descritto in modo efficace il comportamento esterno di un caricatore di file di configurazione, in primo luogo utilizzando l'approccio di Taylor, e quindi utilizzando un approccio BDD tradizionale. Successivamente, abbiamo implementato la funzione, utilizzando phpspec per progettare e descrivere il comportamento interno. L'esempio su cui abbiamo lavorato è piuttosto semplice, ma abbiamo coperto le basi. Se abbiamo bisogno di più complessità, possiamo semplicemente estendere ciò che abbiamo già. Usando BDD, abbiamo almeno tre opzioni:

  1. Se osserviamo un bug o abbiamo bisogno di cambiare alcuni interni del nostro software, possiamo descriverlo usando phpspec. Scrivi un esempio negativo che mostri il bug e scrivi il codice necessario per arrivare al verde.
  2. Se abbiamo bisogno di aggiungere un nuovo caso d'uso a ciò che abbiamo, possiamo aggiungere uno scenario  config.feature. Possiamo quindi lavorare iterativamente attraverso ogni fase, usando Behat e phpecec.
  3. Se abbiamo bisogno di implementare una nuova funzionalità, come il supporto dei file di configurazione YAML, possiamo scrivere una nuova funzionalità e ricominciare da capo, usando l'approccio che abbiamo usato in questo tutorial.

Con questa configurazione di base, non abbiamo scuse per non scrivere un test o una specifica in errore, prima di scrivere il nostro codice. Quello che abbiamo costruito è ora coperto da test, che renderà molto più facile lavorare con esso in futuro. Aggiungete a ciò che il nostro codice è anche completamente documentato. I casi d'uso previsti sono descritti in inglese semplice e le lavorazioni interne sono descritte nelle nostre specifiche. Queste due cose renderanno un gioco da ragazzi per gli altri sviluppatori capire e lavorare con il codebase.

Spero che questo tutorial ti abbia aiutato a capire meglio come BDD può essere usato in un contesto PHP, con Behat e phpecec. Se avete domande o commenti, per favore fateli pubblicare qui sotto nella sezione commenti.

Grazie per la lettura!