Laravel, BDD og deg La oss komme i gang

Velkommen til denne serien om å utvikle Laravel-applikasjoner ved hjelp av en adferdsdrevet utvikling (BDD) tilnærming. Full Stack BDD kan virke komplisert og skremmende. Det er like mange måter å gjøre det på som det er utviklere. 

I denne serien vil jeg gå deg gjennom tilnærmingen min med å bruke Behat og PhpSpec til å designe en Laravel-applikasjon fra grunnen av.

Det er mange ressurser på BDD generelt, men Laravel-spesifikt materiale er vanskelig å finne. Derfor vil vi i denne serien fokusere mer på Laravel-relaterte aspekter og mindre på de generelle tingene som du kan lese om mange andre steder.

Beskrive oppførsel

Når vi beskriver adferd, som også kalles for å skrive historier og spesifikasjoner, vil vi bruke en utenforliggende tilnærming. Dette betyr at hver gang vi bygger en ny funksjon, begynner vi å skrive den generelle brukerhistorien. Dette er normalt fra klientene eller interessentperspektivet. 

Hva forventer vi å skje når vi gjør dette? 

Vi har ikke lov til å skrive noen kode før vi har et sviktende, rødt steg for eksempel, med mindre vi refactoring eksisterende kode. 

Noen ganger vil det være nødvendig å løse iterativt en liten del av en historie eller funksjon (to ord som jeg bruker om hverandre) som vi jobber på. Dette vil ofte bety å skrive spesifikasjoner med PhpSpec. Noen ganger vil det ta mange iterasjoner på et integrasjons- eller enhetsnivå før hele historien (på et akseptnivå) går forbi. Alt dette høres veldig komplisert, men det er egentlig ikke. Jeg er en stor tro på å lære ved å gjøre, så jeg tror at alt vil gjøre mer fornuftig når vi begynner å skrive noen faktisk kode.

Vi skal skrive historier og spesifikasjoner på fire forskjellige nivåer:

1. Godkjenning

Mesteparten av tiden fungerer vår funksjonelle suite som vårt akseptelag. Måten vi vil beskrive våre funksjoner i vår funksjonelle suite, vil ligner på hvordan vi vil skrive aksepthistorier (ved hjelp av en automatisert nettleseramme) og vil som sådan skape mye duplisering. 

Så lenge historiene beskriver oppførselen fra kundens synspunkt, tjener de som aksepthistorier. Vi vil bruke Symfony DomCrawler til å teste utdataene fra vår søknad. Senere i serien kan det hende at vi må teste gjennom en faktisk nettleser som også kan kjøre JavaScript. Testing gjennom nettleseren legger til noen nye bekymringer, siden vi må sørge for at vi laster timetestmiljøet når suiten kjøres.

2. Funksjonell

I vår funksjonelle suite har vi tilgang til Laravel-applikasjonen, som er veldig praktisk. Først av alt gjør det det enkelt å skille mellom miljøer. For det andre, ikke å gå gjennom en nettleser, gjør vår testpakke mye raskere. Når vi vil implementere en ny funksjon, vil vi skrive en historie i vår funksjonelle suite ved hjelp av Behat.

3. Integrasjon

Vår integrasjonspakke vil teste oppførselen til kjernen av applikasjonen vår, som ikke nødvendigvis trenger tilgang til Laravel. Integrasjonspakken vil normalt være en blanding av Behat-historier og PhpSpec-spesifikasjoner.

4. Enhet

Våre enhetstester vil bli skrevet i PhpSpec og vil teste isolerte små enheter av applikasjonskjernen. Våre enheter, verdiobjekter etc. har alle spesifikasjoner.

Saken

Gjennom hele denne serien, med utgangspunkt i neste artikkel, vil vi bygge et system for sporingstid. Vi starter med å beskrive atferd fra utsiden ved å skrive Behat-funksjoner. Den interne oppførselen til søknaden vår vil bli beskrevet ved hjelp av PhpSpec. 

Sammen vil disse to verktøyene hjelpe oss med å føle oss komfortable med kvaliteten på applikasjonen vi bygger. Vi vil primært skrive funksjoner og spesifikasjoner på tre nivåer: 

  1. funksjonell
  2. Integrering
  3. Enhet


I vår funksjonelle pakke vil vi krype HTTP-responsene i applikasjonen vår i en hodeløs modus, noe som betyr at vi ikke vil gå gjennom nettleseren. Dette vil gjøre det lettere å samhandle med Laravel og gjøre vår funksjonelle suite tjene som vårt akseptelag også. 

Senere, hvis vi ender med å ha et mer komplisert brukergrensesnitt, og det kan hende at vi må teste noen JavaScript, kan vi også legge til en dedikert godkjenningssoalett. Denne serien er fortsatt i arbeid, så vær så snill å slippe forslagene dine i kommentarfeltet.

Vår oppsett

Legg merke til at for denne opplæringen antar jeg at du har en fersk installasjon av Laravel (4.2) oppe. Fortrinnsvis bruker du Laravel Homestead, som er det jeg brukte da jeg skrev denne koden.

Før vi begynner med noe virkelig arbeid, la oss sørge for at vi har Behat og PhpSpec oppe og løpende. Først skjønt, liker jeg å gjøre litt rengjøring når jeg starter et nytt laravelprosjekt og slett ting jeg ikke trenger:

git rm -r app / tester / phpunit.xml CONTRIBUTING.md

Hvis du sletter disse filene, må du sørge for at du oppdaterer composer.json arkiver deretter:

"autoload": "klassekart": ["app / kommandoer", "app / kontroller", "app / modeller", "app / database / migrasjoner", "app / database / frø"], 

Og selvfølgelig:

$ komponent dump-autoload

Nå er vi klare til å trekke inn BDD-verktøyene vi trenger. Bare legg til en require-dev delen til din composer.json:

"required": "behat / behat": "~ 3.0", "phpspec / phpspec": "~ 2.0", "phpunit / phpunit ":" ~ 4,1 ", 

"Hvorfor drar vi i PHPUnit?" kan du tenke? Vi kommer ikke til å skrive gode ol 'PHPUnit test tilfeller i denne serien, men påstandene er et praktisk verktøy sammen med Behat. Vi vil se det senere i denne artikkelen når vi skriver vår første funksjon.

Husk å oppdatere dine avhengigheter etter endring composer.json:

$ komponent oppdatering --dev

Vi er nesten ferdige med å installere og sette opp ting. PhpSpec fungerer ut av esken:

$ leverandør / bin / phpspec kjøre 0 spesifikasjoner 0 eksempler 0ms

Men Behat trenger å gjøre en rask løp med --i det alternativ for å sette alt opp:

$ vendor / bin / behat --init + d funksjoner - plasser dine * .feature filer her + d funksjoner / bootstrap - plasser kontekst klassene dine her + f features / bootstrap / FeatureContext.php - plasser definisjoner, transformasjoner og kroker her $ leverandør / bin / behat Ingen scenarier Ingen trinn 0m0.14s (12.18Mb)

Den første kommandoen skapt et skinnende nytt FeatureContext klasse, der vi kan skrive de trinndefinisjonene som trengs for funksjonene våre:

Skriver vår første funksjon

Vår første funksjon vil være veldig enkel: Vi vil ganske enkelt sørge for at vår nye Laravel installasjon hilser oss med "du er kommet." på hjemmesiden. Jeg legger meg inn litt dumt gitt gå også, Gitt jeg er logget inn, som bare tjener til å vise hvor lett samspill med Laravel i våre funksjoner er.

Teknisk vil jeg kategorisere denne typen funksjon som en funksjonell test, siden den samhandler med rammen, men det tjener også som en aksepttest, siden vi ikke ville se noen andre resultater fra å kjøre en lignende test gjennom et nettleserprøveverktøy. For nå vil vi holde fast i vår funksjonelle test suite.

Gå videre og opprett en welcome.feature filen og legg den inn funksjoner / funksjonell:

# features / functional / welcome.feature Feature: Innbydende utvikler Som Laravel-utvikler For å kunne begynne et nytt prosjekt må jeg bli møtt ved ankomst Scenario: Hilsenutvikler på hjemmesiden Gitt Jeg er innlogget Når jeg besøker "/" så jeg bør se "du er kommet." 

Ved å sette funksjonelle funksjoner i a funksjonell katalog, det blir lettere for oss å administrere våre suiter senere. Vi ønsker ikke integreringstypefunksjoner som ikke krever at Laravel må vente på den sakte funksjonelle pakken. 

Jeg liker å holde ting rent og pent, så jeg tror vi burde ha en dedikert funksjonskontekst for vår funksjonelle suite som kan gi oss tilgang til Laravel. Du kan bare gå videre og kopiere eksisterende FeatureContext fil og endre klassenavnet til LaravelFeatureContext. For at dette skal fungere, trenger vi også en behat.yml konfigurasjonsfil. 

Opprett en i rotkatalogen av prosjektet ditt og legg til følgende:

standard: suiter: funksjonelle: stier: [% paths.base% / features / functional] sammenhenger: [LaravelFeatureContext] 

Jeg tror YAML her er ganske selvforklarende. Vår funksjonelle suite vil se etter funksjoner i funksjonell katalog og kjør dem gjennom LaravelFeatureContext.

Hvis vi prøver å kjøre Behat på dette punktet, vil det fortelle oss å implementere de nødvendige trinndefinisjonene. Vi kan ha Behat legge til de tomme stillasemetodene til LaravelFeatureContext med følgende kommando:

$ vendor / bin / behat -dry-run --append-snippets $ vendor / bin / behat Funksjon: Innbydende utvikler Som Laravel-utvikler For å kunne begynne et nytt prosjekt, må jeg bli møtt etter arival Scenario: Hilsen utvikler på Hjemmeside # funksjoner / funksjonell / velkommen.featur: 6 Gitt jeg er innlogget # LaravelFeatureContext :: iAmLoggedIn () TODO: skriv ventende definisjon Når jeg besøker "/" # LaravelFeatureContext :: iVisit () Så skal jeg se "Du er kommet. " # LaravelFeatureContext :: iShouldSee () 1 scenario (1 venter) 3 trinn (1 venter, 2 hoppet over) 0m0.28s (12.53Mb)

Og nå, som du ser fra produksjonen, er vi klare til å begynne å implementere det første av trinnene våre: Gitt jeg er logget inn.

PHPUnit test saken som sendes med Laravel lar oss gjøre ting som $ Dette-> være ($ bruker), som logger inn i en gitt bruker. Til slutt ønsker vi å kunne samhandle med Laravel som om vi brukte PHPUnit, så la oss gå videre og skrive trinndefinisjonskoden "vi skulle ønske vi hadde":

/ ** * @Given Jeg er logget inn * / offentlig funksjon iAmLoggedIn () $ user = new User; $ Dette-> være ($ bruker);  

Dette vil ikke fungere selvfølgelig, siden Behat har ingen anelse om Laravel-spesifikke ting, men jeg vil vise deg på et øyeblikk hvor lett det er å få Behat og Laravel til å leke pent sammen.

Hvis du tar en titt i Laravel kilden og finner Belyse \ Foundation \ Testing \ Testcase klassen, som er klassen som standard test saken strekker seg fra, vil du se at starter fra Laravel 4.2, har alt blitt flyttet til et trekk. De ApplicationTrait er nå ansvarlig for oppstart av en applikasjon eksempel, sette opp en HTTP-klient og gi oss noen hjelpemetoder, for eksempel være()

Dette er ganske kult, hovedsakelig fordi det betyr at vi bare kan trekke den inn i våre Behat-sammenhenger med nesten ingen oppsett påkrevd. Vi har også tilgang til AssertionsTrait, men dette er fortsatt knyttet til PHPUnit.

Når vi trekker inn egenskapen, må vi gjøre to ting. Vi må ha en SETUP () metode, som den iBelyse \ Foundation \ Testing \ Testcase klasse, og vi trenger en createApplication () metode, som den i standard Laravel test saken. Egentlig kan vi bare kopiere disse to metodene og bruke dem direkte. 

Det er bare en ting å legge merke til: I PHPUnit, metoden SETUP () vil automatisk bli kalt før hver test. For å oppnå det samme i Behat, kan vi bruke @BeforeScenario merknad.

Legg til følgende i din LaravelFeatureContext:

bruk Illuminate \ Foundation \ Testing \ ApplicationTrait; / ** * Denne kontekstklasse. * / klasse LaravelFeatureContext implementerer SnippetAcceptingContext / ** * Ansvarlig for å levere en Laravel app instans. * / bruk ApplicationTrait; / ** * Initialiserer kontekst. * * Hvert scenario får sin egen kontekstobjekt. * Du kan også sende vilkårlige argumenter til kontekstkonstruktøren gjennom behat.yml. * / offentlig funksjon __construct ()  / ** * @BeforeScenario * / public function setUp () if (! $ this-> app) $ this-> refreshApplication ();  / ** * Oppretter applikasjonen. * * @return \ Symfony \ Component \ HttpKernel \ HttpKernelInterface * / offentlig funksjon createApplication () $ unitTesting = true; $ testEnvironment = 'testing'; retur krever __DIR __. '/ ... / ... /bootstrap/start.php';  

Ganske enkelt, og se hva vi får når vi løper Behat:

$ vendor / bin / behat Funksjon: Innbydende utvikler Som Laravel-utvikler For å kunne begynne et nytt prosjekt, må jeg bli møtt etter arival Scenario: Hilsenutvikler på hjemmesiden # funksjoner / funksjonell / velkommen.featur: 6 Gitt jeg er logget inn # LaravelFeatureContext :: iAmLoggedIn () Når jeg besøker "/" # LaravelFeatureContext :: iVisit () TODO: skriv ventende definisjon Da skal jeg se "Du er kommet." # LaravelFeatureContext :: iShouldSee () 1 scenario (1 ventende) 3 trinn (1 bestått, 1 ventet, 1 hoppet over) 0m0.73s (17.92Mb)

Et grønt første skritt, noe som betyr at oppsettet vårt fungerer!

Deretter kan vi implementere Når jeg besøker skritt. Denne er super enkel, og vi kan bare bruke anrop()metode som ApplicationTrait gir. En linje med kode vil få oss der:

/ ** * @Når jeg besøker: uri * / offentlig funksjon iVisit ($ uri) $ this-> call ('GET', $ uri);  

Det siste trinnet, Da skal jeg se, tar litt mer og vi må trekke inn to avhengigheter. Vi trenger PHPUnit for påstanden, og vi vil trenge Symfony DomCrawler å søke etter "du er kommet." tekst.

Vi kan implementere det slik:

bruk PHPUnit_Framework_Assert som PHPUnit; bruk Symfony \ Component \ DomCrawler \ Crawler; ... / ** * @Then bør jeg se: tekst * / offentlig funksjon iShouldSee ($ tekst) $ crawler = ny Crawler ($ this-> client-> getResponse () -> getContent ()); PHPUnit :: assertCount (1, $ crawler-> filterXpath ("// text () [. = '$ Text']"));  

Dette er ganske mye den samme koden som du ville skrive hvis du brukte PHPUnit. De filterXpath () del er litt forvirrende, og vi vil ikke bekymre oss om det nå, siden det er litt utenfor omfanget av denne artikkelen. Bare stol på meg at det fungerer.

Kjører Hvor en siste gang er gode nyheter:

$ vendor / bin / behat Funksjon: Innbydende utvikler Som Laravel-utvikler For å kunne begynne et nytt prosjekt, må jeg bli møtt etter arival Scenario: Hilsenutvikler på hjemmesiden # funksjoner / funksjonell / velkommen.featur: 6 Gitt jeg er logget inn # LaravelFeatureContext :: iAmLoggedIn () Når jeg besøker "/" # LaravelFeatureContext :: iVisit () Så skal jeg se "Du er kommet." # LaravelFeatureContext :: iShouldSee () 1 scenario (1 bestått) 3 trinn (3 passert) 0m0.82s (19.46Mb)

Funksjonen fungerer som forventet og utvikleren blir møtt ved ankomst.

Konklusjon

Den komplette LaravelFeatureContext bør nå se ut som dette:

app) $ this-> refreshApplication ();  / ** * Oppretter applikasjonen. * * @return \ Symfony \ Component \ HttpKernel \ HttpKernelInterface * / offentlig funksjon createApplication () $ unitTesting = true; $ testEnvironment = 'testing'; retur krever __DIR __. '/ ... / ... /bootstrap/start.php';  / ** * @Given Jeg er logget inn * / offentlig funksjon iAmLoggedIn () $ user = new User; $ Dette-> være ($ bruker);  / ** * @Når jeg besøker: uri * / offentlig funksjon iVisit ($ uri) $ this-> call ('GET', $ uri);  / ** * @Then skal jeg se: tekst * / offentlig funksjon iShouldSee ($ tekst) $ crawler = ny Crawler ($ this-> client-> getResponse () -> getContent ()); PHPUnit :: assertCount (1, $ crawler-> filterXpath ("// text () [. = '$ Text']"));  

Vi har nå et veldig fint fundament å bygge videre på når vi fortsetter å utvikle vårt nye Laravel-program ved hjelp av BDD. Jeg håper jeg har vist deg hvor lett det er å få Laravel og Behat å leke pent sammen. 

Vi har tatt på mange forskjellige temaer i denne første artikkelen. Du trenger ikke å bekymre deg, vi vil ta en mer grundig titt på alt ettersom serien fortsetter. Hvis du har spørsmål eller forslag, vennligst legg igjen en kommentar.