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.
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.
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.
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:
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.
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!
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.
Prima di iniziare, ho due punti importanti da fare riguardo alle definizioni dei passi:
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.phpSuccessivamente, abbiamo un
E
passo, che in questo caso è solo un alias perDato
. 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) 7msCon questi comandi, phpspec ha creato per noi i seguenti due file:
spec / '- ConfigSpec.php src /' - Config.phpQuesto 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ò:
- 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.- 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 ilimpostato()
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) 9msOra, 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) 9msQuesto 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 tornandonullo
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 tornandonullo
. 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) 9msPer 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) 9msE 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:
- 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.
- 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.- 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!