Benvenuti in questa serie sullo sviluppo di applicazioni Laravel utilizzando un approccio di sviluppo comportamentale (BDD). Lo stack completo BDD può sembrare complicato e intimidatorio. Ci sono tanti modi per farlo, come ci sono gli sviluppatori.
In questa serie, ti guiderò attraverso il mio approccio all'uso di Behat e PhpSpec per progettare un'applicazione Laravel da zero.
Ci sono molte risorse sul BDD in generale, ma il materiale specifico di Laravel è difficile da trovare. Pertanto, in questa serie, ci concentreremo di più sugli aspetti relativi a Laravel e meno sulle cose generali che puoi leggere su molti altri luoghi.
Quando descriviamo il comportamento, che è anche noto come scrivere storie e specifiche, useremo un approccio esterno. Ciò significa che ogni volta che creeremo una nuova funzionalità, inizieremo scrivendo la storia utente complessiva. Questo è normalmente dal punto di vista dei clienti o degli stakeholder.
Cosa ci aspettiamo che succedano quando lo facciamo?
Non siamo autorizzati a scrivere alcun codice finché non avremo un passaggio rosso negativo, ad esempio, a meno che non stiamo effettuando il refactoring del codice esistente.
A volte, sarà necessario risolvere iterativamente una piccola parte di una storia o di una caratteristica (due parole che uso in modo intercambiabile) su cui stiamo lavorando. Ciò significa spesso scrivere specifiche con PhpSpec. A volte ci vorranno molte iterazioni a livello di integrazione o unità prima che l'intera storia (a livello di accettazione) passi. Sembra tutto molto complicato ma in realtà non lo è. Sono un grande sostenitore dell'apprendimento, quindi penso che tutto avrà più senso una volta iniziato a scrivere un codice reale.
Scriveremo storie e specifiche su quattro diversi livelli:
La maggior parte delle volte, la nostra suite funzionale fungerà da nostro livello di accettazione. Il modo in cui descriveremo le nostre funzionalità nella nostra suite funzionale sarà molto simile a come scriverei storie di accettazione (utilizzando un framework browser automatico) e creeremmo un sacco di duplicazioni.
Finché le storie descrivono il comportamento dal punto di vista del cliente, servono come storie di accettazione. Useremo Symfony DomCrawler per testare l'output della nostra applicazione. Più avanti nella serie, potremmo scoprire che abbiamo bisogno di testare un browser reale in grado di eseguire JavaScript. Il test tramite il browser aggiunge alcune nuove preoccupazioni, poiché è necessario assicurarsi di caricare l'ambiente di test delle ore quando viene eseguita la suite.
Nella nostra suite funzionale, avremo accesso all'applicazione Laravel, che è molto conveniente. Innanzitutto, semplifica la differenziazione tra gli ambienti. In secondo luogo, non passare attraverso un browser rende la nostra suite di test molto più veloce. Ogni volta che vogliamo implementare una nuova funzionalità, scriveremo una storia nella nostra suite funzionale usando Behat.
La nostra suite di integrazione testerà il comportamento della parte centrale della nostra applicazione che non ha necessariamente bisogno di accedere a Laravel. La suite di integrazione sarà normalmente una miscela di storie Behat e specifiche PhpSpec.
I nostri test unitari verranno scritti in PhpSpec e testeranno piccole unità isolate del core dell'applicazione. Le nostre entità, oggetti di valore, ecc. Avranno tutte le specifiche.
In questa serie, a partire dal prossimo articolo, costruiremo un sistema per il monitoraggio del tempo. Inizieremo descrivendo il comportamento dall'esterno scrivendo le funzionalità di Behat. Il comportamento interno della nostra applicazione verrà descritto utilizzando PhpSpec.
Insieme, questi due strumenti ci aiuteranno a sentirci a nostro agio con la qualità dell'applicazione che stiamo costruendo. Scriveremo principalmente caratteristiche e specifiche su tre livelli:
Nella nostra suite funzionale, eseguiremo la scansione delle risposte HTTP della nostra applicazione in modalità headless, il che significa che non passeremo attraverso il browser. Ciò renderà più semplice l'interazione con Laravel e renderà la nostra suite funzionale anche il nostro livello di accettazione.
Più tardi, se finiremo per avere un'interfaccia utente più complicata e potrebbe essere necessario testare anche alcuni JavaScript, potremmo aggiungere una suite di accettazione dedicata. Questa serie è ancora work-in-progress, quindi sentitevi liberi di lasciare i vostri suggerimenti nella sezione commenti.
Nota che per questo tutorial, presumo tu abbia una nuova installazione di Laravel (4.2) attiva e funzionante. Preferibilmente stai usando Laravel Homestead, che è quello che ho usato quando ho scritto questo codice.
Prima di iniziare un vero lavoro, assicuriamoci di avere Behat e PhpSpec attivi e funzionanti. Prima però, mi piace fare un po 'di pulizia ogni volta che avvio un nuovo progetto laravel ed elimina le cose che non mi servono:
git rm -r app / tests / phpunit.xml CONTRIBUTING.md
Se elimini questi file, assicurati di aggiornare il tuo composer.json
file di conseguenza:
"autoload": "classmap": ["app / comandi", "app / controller", "app / modelli", "app / database / migrazioni", "app / database / semi"],
E naturalmente:
$ composer dump-autoload
Ora siamo pronti per inserire gli strumenti BDD di cui abbiamo bisogno. Basta aggiungere un require-dev
sezione al tuo composer.json
:
"require": "laravel / framework": "4.2. *", "require-dev": "behat / behat": "~ 3.0", "phpspec / phpspec": "~ 2.0", "phpunit / phpunit ":" ~ 4.1 ",
"Perché stiamo inserendo PHPUnit?" potresti pensare? Non scriveremo buoni casi di test PHPUnit in questa serie, ma le asserzioni sono uno strumento utile insieme a Behat. Lo vedremo più avanti in questo articolo quando scriviamo il nostro primo lungometraggio.
Ricordati di aggiornare le tue dipendenze dopo la modifica composer.json
:
$ compositore update --dev
Abbiamo quasi finito di installare e installare materiale. PhpSpec funziona fuori dalla scatola:
$ vendor / bin / phpspec esegui 0 specifiche 0 esempi 0ms
Ma Behat deve fare una corsa veloce con il --dentro
opzione per impostare tutto:
$ vendor / bin / behat --init + d features - posiziona qui i tuoi file * .feature + d features / bootstrap - inserisci qui le tue classi di contesto + f features / bootstrap / FeatureContext.php - inserisci qui definizioni, trasformazioni e ganci $ vendor / bin / behat Nessun scenario Nessun passaggio 0m0.14s (12.18Mb)
Il primo comando ha creato un nuovo splendente FeatureContext
classe, dove possiamo scrivere le definizioni di passaggio necessarie per le nostre funzionalità:
Scrivere la nostra prima caratteristica
La nostra prima funzione sarà molto semplice: ci assicureremo semplicemente che la nostra nuova installazione Laravel ci saluti con un "Sei arrivato". sulla homepage. Ho messo piuttosto sciocco
Dato
passo pure,Dato che sono loggato
, che serve solo a mostrare quanto sia facile interagire con Laravel nelle nostre funzionalità.Tecnicamente, classificherei questo tipo di funzionalità come test funzionale, dal momento che interagisce con il framework, ma serve anche come test di accettazione, dal momento che non vedremmo risultati diversi dall'esecuzione di un test simile attraverso uno strumento di test del browser. Per ora continueremo con la nostra suite di test funzionale.
Vai avanti e crea un
welcome.feature
file e inserisciloCaratteristiche / funzionale
:# features / functional / welcome.feature Caratteristica: sviluppatore di benvenuto Come sviluppatore Laravel Per iniziare in modo proattivo un nuovo progetto ho bisogno di essere accolto all'arrivo Scenario: sviluppatore di saluti su homepage Dato che sono connesso Quando visito "/" Allora I dovrebbe vedere "Sei arrivato."Inserendo le caratteristiche funzionali in a
funzionale
directory, sarà più facile per noi gestire le nostre suite in seguito. Non vogliamo funzionalità di tipo di integrazione che non richiedano che Laravel debba attendere la suite funzionale lenta.Mi piace mantenere le cose belle e pulite, quindi credo che dovremmo avere un contesto di funzionalità dedicato per la nostra suite funzionale che può darci accesso a Laravel. Puoi semplicemente andare avanti e copiare l'esistente
FeatureContext
file e cambia il nome della classe inLaravelFeatureContext
. Perché funzioni, abbiamo anche bisogno di unbehat.yml
file di configurazione.Creane uno nella directory principale del tuo progetto e aggiungi quanto segue:
default: suites: functional: paths: [% paths.base% / features / functional] contesti: [LaravelFeatureContext]Penso che lo YAML qui sia abbastanza auto-esplicativo. La nostra suite funzionale cercherà funzionalità nel
funzionale
directory ed eseguirli attraverso ilLaravelFeatureContext
.Se proviamo a eseguire Behat a questo punto, ci dirà di implementare le necessarie definizioni dei passaggi. Possiamo avere Behat aggiungere i metodi dello scaffold vuoto al
LaravelFeatureContext
con il seguente comando:$ vendor / bin / behat --dry-run --append-snippets $ vendor / bin / behat Caratteristica: sviluppatore di benvenuto Come sviluppatore Laravel Per iniziare in modo proberale un nuovo progetto ho bisogno di essere accolto su uno scenario arivale: lo sviluppatore di saluto su homepage # features / functional / welcome.feature: 6 Dato che sono loggato # LaravelFeatureContext :: iAmLoggedIn () TODO: scrivi definizione in attesa Quando visito "/" # LaravelFeatureContext :: iVisit () Allora dovrei vedere "Sei arrivato. " # LaravelFeatureContext :: iShouldSee () 1 scenario (1 in attesa) 3 passaggi (1 in sospeso, 2 saltati) 0m0.28s (12.53Mb)E ora, come puoi vedere dall'output, siamo pronti per iniziare a implementare il primo dei nostri passi:
Dato che sono loggato
.Il test case PHPUnit fornito con Laravel ci consente di fare cose del genere
$ This-> essere ($ user)
, che registra un determinato utente. In definitiva, vogliamo essere in grado di interagire con Laravel come se stessimo usando PHPUnit, quindi andiamo avanti e scriviamo il codice di definizione del passo "ci piacerebbe avere":/ ** * @Given Ho effettuato l'accesso * / public function iAmLoggedIn () $ user = new User; $ This-> essere ($ user);Questo ovviamente non funzionerà, dal momento che Behat non ha idea di cose specifiche su Laravel, ma ti mostrerò in un secondo come è facile far suonare Behat e Laravel insieme.
Se dai un'occhiata alla fonte di Laravel e trovi il
Illuminare \ Foundation \ Testing \ TestCase
classe, che è la classe da cui si estende il test case predefinito, vedrai che partendo da Laravel 4.2, tutto è stato spostato su un tratto. IlApplicationTrait
è ora responsabile dell'avvio di unApplicazione
istanza, impostando un client HTTP e darci alcuni metodi di supporto, come ad esempioessere()
.Questo è piuttosto interessante, soprattutto perché significa che possiamo semplicemente inserirlo nei nostri contesti Behat con quasi nessuna configurazione richiesta. Abbiamo anche accesso a
AssertionsTrait
, ma questo è ancora legato a PHPUnit.Quando inseriamo il tratto, dobbiamo fare due cose. Abbiamo bisogno di avere un
impostare()
metodo, come quello nelIlluminare \ Foundation \ Testing \ TestCase
classe, e abbiamo bisogno di acreateApplication ()
metodo, come quello nel caso di test Laravel predefinito. In realtà possiamo solo copiare questi due metodi e usarli direttamente.C'è solo una cosa da notare: in PHPUnit, il metodo
impostare()
verrà chiamato automaticamente prima di ogni test. Per ottenere lo stesso risultato in Behat, possiamo usare il@BeforeScenario
annotazione.Aggiungi il seguente al tuo
LaravelFeatureContext
:utilizzare Illuminate \ Foundation \ Testing \ ApplicationTrait; / ** * Behat classe di contesto. * / class LaravelFeatureContext implementa SnippetAcceptingContext / ** * Responsabile di fornire un'istanza dell'app Laravel. * / usa ApplicationTrait; / ** * Inizializza il contesto. * * Ogni scenario ottiene il proprio oggetto di contesto. * È anche possibile passare argomenti arbitrari al costruttore di contesto tramite behat.yml. * / public function __construct () / ** * @BeforeScenario * / public function setUp () if (! $ this-> app) $ this-> refreshApplication (); / ** * Crea l'applicazione. * * @return \ Symfony \ Component \ HttpKernel \ HttpKernelInterface * / public function createApplication () $ unitTesting = true; $ testEnvironment = 'testing'; return richiede __DIR __. '/ ... / ... /bootstrap/start.php';Abbastanza facile, e guarda cosa otteniamo quando eseguiamo Behat:
$ vendor / bin / behat Caratteristica: sviluppatore accogliente Come sviluppatore Laravel Per iniziare in modo proattivo un nuovo progetto ho bisogno di essere accolto su uno scenario arivale: lo sviluppatore di saluto su home page # features / functional / welcome.feature: 6 Dato che sono loggato # LaravelFeatureContext :: iAmLoggedIn () Quando visito "/" # LaravelFeatureContext :: iVisit () TODO: scrivere la definizione in sospeso Quindi dovrei vedere "Sei arrivato." # LaravelFeatureContext :: iShouldSee () 1 scenario (1 in sospeso) 3 passaggi (1 superato, 1 in sospeso, 1 ignorato) 0m0.73s (17.92Mb)Un primo passo verde, il che significa che il nostro setup funziona!
Successivamente, possiamo implementare il
Quando visito
passo. Questo è semplicissimo e possiamo semplicemente usare ilchiamata()
metodo che ilApplicationTrait
fornisce. Una riga di codice ci porterà lì:/ ** * @Quando visito: uri * / public function iVisit ($ uri) $ this-> call ('GET', $ uri);L'ultimo passo,
Allora dovrei vedere
, ci vuole un po 'di più e abbiamo bisogno di tirare dentro due dipendenze. Avremo bisogno di PHPUnit per l'asserzione e avremo bisogno di Symfony DomCrawler per cercare "Sei arrivato". testo.Possiamo implementarlo in questo modo:
usa PHPUnit_Framework_Assert come PHPUnit; usa Symfony \ Component \ DomCrawler \ Crawler; ... / ** * @Then dovrei vedere: text * / public function iShouldSee ($ text) $ crawler = new Crawler ($ this-> client-> getResponse () -> getContent ()); PHPUnit :: assertCount (1, $ crawler-> filterXpath ("// text () [. = '$ Text']"));Questo è praticamente lo stesso codice che scriverebbe se si stesse usando PHPUnit. Il
filterXpath ()
parte è un po 'confusa e non ci preoccuperemo ora, dato che è un po' fuori dal campo di applicazione di questo articolo. Fidati di me che funziona.Eseguire Behat un'ultima volta è una buona notizia:
$ vendor / bin / behat Caratteristica: sviluppatore accogliente Come sviluppatore Laravel Per iniziare in modo proattivo un nuovo progetto ho bisogno di essere accolto su uno scenario arivale: lo sviluppatore di saluto su home page # features / functional / welcome.feature: 6 Dato che sono loggato # LaravelFeatureContext :: iAmLoggedIn () Quando visito "/" # LaravelFeatureContext :: iVisit () Allora dovrei vedere "Sei arrivato." # LaravelFeatureContext :: iShouldSee () 1 scenario (1 superato) 3 passaggi (3 passati) 0m0.82s (19.46Mb)La funzione funziona come previsto e lo sviluppatore viene accolto all'arrivo.
Conclusione
Il completo
LaravelFeatureContext
dovrebbe ora assomigliare a questo:app) $ this-> refreshApplication (); / ** * Crea l'applicazione. * * @return \ Symfony \ Component \ HttpKernel \ HttpKernelInterface * / public function createApplication () $ unitTesting = true; $ testEnvironment = 'testing'; return richiede __DIR __. '/ ... / ... /bootstrap/start.php'; / ** * @Given Ho effettuato l'accesso * / public function iAmLoggedIn () $ user = new User; $ This-> essere ($ user); / ** * @Quando visito: uri * / public function iVisit ($ uri) $ this-> call ('GET', $ uri); / ** * @Quindi dovrei vedere: text * / public function iShouldSee ($ text) $ crawler = new Crawler ($ this-> client-> getResponse () -> getContent ()); PHPUnit :: assertCount (1, $ crawler-> filterXpath ("// text () [. = '$ Text']"));Ora abbiamo una base davvero interessante su cui continuare a sviluppare la nostra nuova applicazione Laravel usando BDD. Spero di averti dimostrato quanto sia facile far suonare piacevolmente Laravel e Behat insieme.
Abbiamo toccato molti argomenti diversi in questo primo articolo. Non c'è bisogno di preoccuparsi, daremo un'occhiata più approfondita a tutto mentre la serie continua. Se avete domande o suggerimenti, si prega di lasciare un commento.