En BDD arbeidsflyt med Behat og Phpspec

I denne veiledningen vil vi se på to forskjellige BDD-verktøy, Behat og phpspec, og se hvordan de kan støtte deg i utviklingsprosessen. Lære BDD kan være forvirrende. Ny metode, nye verktøy og mange spørsmål, for eksempel "hva skal du teste?" og "hvilke verktøy som skal brukes?". Jeg håper at dette ganske enkle eksempelet vil gi deg ideer til hvordan du kan innlemme BDD i din egen arbeidsflyt.

Min inspirasjon

Jeg ble inspirert til å skrive denne opplæringen av Taylor Otwell, skaperen av Laravel-rammeverket. Flere ganger har jeg hørt Taylor forklare hvorfor han for det meste ikke gjør TDD / BDD ved å si at han liker å planlegge ut API-en av koden sin, før han faktisk begynner å implementere den. Jeg har hørt dette fra mange utviklere, og hver gang jeg tenker på meg selv: "Men det er den perfekte brukssaken til TDD / BDD!". Taylor sier at han liker å kartlegge API for sin kode, ved å skrive koden han ønsket han hadde. Han vil da starte kodingen og ikke være fornøyd før han har oppnådd den nøyaktige API. Argumentet er fornuftig hvis du bare tester / spesifiserer på enhetens nivå, men ved hjelp av et verktøy som Behat, begynner du med ekstern oppførsel av programvaren din, som egentlig er så vidt jeg forstår, hva Taylor vil oppnå.

Hva vi skal dekke

I denne opplæringen vil vi bygge en enkel konfigurasjonsfillasterklasse. Vi vil starte med å bruke Taylors tilnærming og deretter skifte til en BDD-tilnærming i stedet. Eksemplene er minimalistiske, men likevel må vi bekymre oss om inventar, statiske metoder etc., så alt i alt, tror jeg de burde være nok til å vise hvordan Behat og phpspec kan utfylle hverandre.

Ansvarsfraskrivelse: Først av alt, er denne artikkelen ikke en starter guide. Det tar utgangspunkt i grunnleggende kunnskaper om BDD, Behat og phpspec. Du har sikkert allerede sett på disse verktøyene, men sliter fortsatt med hvordan du faktisk bruker dem i din daglige arbeidsflyt. Hvis du ønsker å pusse opp på phpspec, ta en titt på min startveiledning. For det andre bruker jeg Taylor Otwell som et eksempel. Jeg vet ikke noe om hvordan Taylor fungerer, i tillegg til det jeg hørte ham si i podcaster osv. Jeg bruker ham som et eksempel fordi han er en fantastisk utvikler (han laget Laravel!) Og fordi han er kjent. Jeg kan like godt ha brukt noen andre, siden de fleste utviklere, inkludert meg selv, ikke gjør BDD hele tiden, ennå. Også, jeg sier ikke at arbeidsflyten Taylor beskriver er dårlig. Jeg synes det er en glimrende ide å sette litt tanke inn i koden før du faktisk skriver den. Denne opplæringen er bare ment å vise BDD-måten å gjøre dette på.

Taylors arbeidsflyt

La oss begynne med å ta en titt på hvordan Taylor kan gå om å designe denne konfigurasjonsfillasteren. Taylor sier at han liker å bare skyte opp en tom tekstfil i redaktøren og deretter skrive hvordan han vil at utviklere skal kunne kommunisere med sin kode (API). I en BDD-kontekst kalles dette normalt å teste ekstern oppførsel av programvare og verktøy som Behat er bra for dette. Vi ser dette på kort tid.

For det første kan Taylor ta en beslutning om konfigurasjonsfilene. Hvordan skal de jobbe? Som i Laravel, la oss bare bruke enkle PHP-arrays. Et eksempel på konfigurasjonsfilen kan se slik ut:

# config.php  'UTC',);

Neste, hvordan skal koden som gjør bruk av dette konfigurasjonsfilarbeidet? La oss gjøre dette på Taylor-veien og bare skrive koden vi ønsker vi hadde:

$ config = Config :: load ('config.php'); $ Config-> get ( 'tidssone'); // returnerer UTC '$ config-> get (' timezone ',' CET '); // returnerer 'CET' hvis 'tidszone' ikke er konfigurert $ config-> set ('timezone', 'GMT'); $ Config-> get ( 'tidssone'); // returnerer 'GMT' 

Ok, så dette ser ganske bra ut. Først har vi en statisk samtale til en laste() funksjon, etterfulgt av tre brukstilfeller: 

  1. Å få "tidssone" fra konfigurasjonsfilen. 
  2. Få en standardverdi hvis "tidssone" ikke er konfigurert ennå. 
  3. Endre et konfigurasjonsalternativ ved innstilling det til noe annet. Vi vil beskrive hvert av disse brukssaken, eller scenarier, med Behat på kort tid.

Det er fornuftig å bruke Behat for slike ting. Behat vil ikke tvinge oss til å ta noen designbeslutninger - vi har phpspec for det. Vi vil ganske enkelt flytte kravene, som vi nettopp har beskrevet, til en Behat trekk for å sikre at vi får det riktig når vi begynner å bygge. Vår behat funksjon vil tjene som en aksept test for våre krav så å si.

Hvis du ser på koden vi skrev, er en annen grunn til å bruke Behat, i stedet for bare phpspec, det statiske anropet. Det er ikke lett å teste statiske metoder, spesielt ikke hvis du bare bruker et verktøy som phpspec. Vi vil se hvordan vi kan gå om dette når vi har både Behat og phpspec tilgjengelig.

Setup

Forutsatt at du bruker Composer, er å sette opp Behat og phspec en super enkel to-trinns prosess.

Først trenger du en grunnleggende composer.json fil. Denne inkluderer Behat og phpspec, og bruker psr-4 til å autoload klassene:

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

Løpe komponent installasjon å hente avhengighetene.

phpspec trenger ikke noen konfigurasjon for å kunne kjøre, mens Behat trenger at du kjører følgende kommando for å generere en grunnleggende stillas:

$ leverandør / bin / behat --init

Det er alt som trengs. Dette kan ikke være din unnskyldning for ikke å gjøre BDD!

Planlegger funksjonene med Behat

Så, nå som vi alle er satt opp, er vi klare til å begynne å gjøre BDD. Fordi gjør BDD betyr å installere og bruke Behat og phpspec, rett?

Så vidt jeg er bekymret, begynte vi allerede å gjøre BDD. Vi har effektivt beskrevet ekstern oppførsel av programvaren vår. Våre "kunder" i dette eksemplet er utviklere, som skal samhandle med vår kode. Med "effektivt" mener jeg at vi har beskrevet oppførselen på en måte de vil forstå. Vi kunne ta koden vi allerede skisserte, sett den i en README-fil, og hver anstendig PHP-utvikler ville forstå bruken av den. Så dette er ganske bra faktisk, men jeg har to viktige ting å merke seg om dette. Først og fremst beskriver beskrivelsen av programvaren ved bruk av kode bare i dette eksempelet fordi "klientene" er programmerere. Normalt tester vi noe som skal brukes av "vanlige" mennesker. Et menneskespråk er bedre enn PHP når vi ønsker å kommunisere med mennesker. For det andre, hvorfor ikke automatisere dette? Jeg skal ikke argumentere for hvorfor dette kan være en god ide.

Som sagt, tror jeg begynner å bruke Behat nå ville være en rimelig beslutning.

Ved hjelp av Behat ønsker vi å beskrive hvert av scenariene som vi skisserte over. Vi ønsker ikke å dekke alle kantsaker involvert i bruk av programvaren. Vi har phpspec tilgjengelig hvis dette skulle være nødvendig for å fikse feil underveis osv. Jeg tror mange utviklere, kanskje inkludert Taylor, føler at de må tenke alt gjennom og bestemme alt før de kan skrive tester og spesifikasjoner. Det er derfor de velger å starte uten BDD, fordi de ikke vil bestemme alt på forhånd. Dette er ikke tilfellet med Behat, siden vi beskriver den eksterne oppførelsen og bruken. For å kunne bruke Behat for å beskrive en funksjon, trenger vi ikke å bestemme noe mer enn i eksemplet ovenfor ved å bruke en rå tekstfil. Vi trenger bare å definere kravene til funksjonen - i dette tilfellet den eksterne API for konfigurasjonsfillasterklassen.

Nå, la oss ta den ovennevnte PHP-koden og slå den til en Behat-funksjon, ved hjelp av det engelske språket (egentlig bruk av Gherkin-språket).

Lag en fil i egenskaper/ katalog, kalt config.feature, og fyll ut følgende scenarier:

Funksjon: Konfigurasjonsfiler For å konfigurere applikasjonen min Som utvikler må jeg kunne lagre konfigurasjonsalternativer i en fil Scenario: Å få et konfigurert alternativ Gitt det er en konfigurasjonsfil Og alternativet 'tidszone' er konfigurert til 'UTC' Når Jeg laster opp konfigurasjonsfilen Da skal jeg få UTC som alternativ for tidszone Scenario: Å få et ikke-konfigurert alternativ med en standardverdi Gitt det er en konfigurasjonsfil Og alternativet 'tidszone' er ikke konfigurert ennå Når jeg laster inn konfigurasjonen fil Da skal jeg få standardverdien 'CET' som 'tidszone' alternativ Scenario: Angi et konfigurasjonsalternativ Gitt det er en konfigurasjonsfil Og alternativet 'tidszone' er konfigurert til 'UTC' Når jeg laster konfigurasjonsfilen Og jeg stiller inn ' tidszone 'konfigurasjonsalternativ til' GMT 'Da skal jeg få' GMT 'som' tidszone 'alternativ 

I denne funksjonen beskriver vi, fra utsiden, hvordan "en utvikler" ville kunne lagre konfigurasjonsalternativer. Vi bryr oss ikke om den interne oppførelsen - vi vil når vi begynner å bruke phpspec. Så lenge denne funksjonen går grønn, bryr vi oss ikke om hva som skjer bak kulissene.

La oss løpe Behat og se hva den tenker på vår funksjon:

$ leverandør / bin / behat - dry-run --append-snippets 

Med denne kommandoen forklarer vi Behat å legge til de nødvendige trinndefinisjonene i vår funksjonskontekst. Jeg vil ikke gå mye i detaljer om Behat, men dette legger til en masse tomme metoder til vår FeatureContext klassen som kartlegger våre funksjonstrinn over.

For eksempel, ta en titt på den trinndefinisjonen som Behat har lagt til for Det er en konfigurasjonsfil trinn som vi bruker som "gitt" trinn i alle tre scenarier:

/ ** * @Given det er en konfigurasjonsfil * / offentlig funksjon thereIsAConfigurationFile () kaste nye PendingException ();  

Nå er alt vi trenger å gjøre er å fylle ut noen kode for å beskrive dette.

Skrive trinndefinisjoner

Før vi begynner har jeg to viktige punkter å gjøre om trinndefinisjonene:

  1. Poenget er å bevise at oppførselen nå, er ikke som vi vil at den skal være. Etter det kan vi begynne å designe vår kode, mesteparten av tiden ved hjelp av phpspec, for å komme til grønt.
  2. Implementeringen av trinndefinisjonene er ikke viktig - vi trenger bare noe som fungerer og oppnår "1". Vi kan refactor senere.

Hvis du kjører leverandør / bin / behat, Du vil se at alle scenarier nå har ventende trinn.

Vi begynner med det første trinnet Gitt det er en konfigurasjonsfil. Vi vil bruke en fixtur av konfigurasjonsfilen, slik at vi kan bruke statisk laste() metode senere. Vi bryr oss om det statiske laste() metode fordi det gir oss en fin API Config :: load (), mye som Laravel fasader. Dette trinnet kunne implementeres på mange måter. For nå tror jeg at vi bare skal sørge for at vi har fixturen tilgjengelig og at den inneholder en matrise:

/ ** * @Given det er en konfigurasjonsfil * / offentlig funksjon thereIsAConfigurationFile () if (! File_exists ('fixtures / config.php')) kaste ny Unntak ("Fil 'fixtures / config.php' ikke funnet") ; $ config = inkluderer 'fixtures / config.php'; hvis (! is_array ($ config)) kaste ny unntak ("File 'fixtures / config.php' skal inneholde en array");  

Vi skal komme til grønt med dette trinnet uten å implementere noen kode i tillegg til å lage fixturen. Formålet med a gitt Trinn er å sette systemet i en kjent tilstand. I dette tilfellet betyr det at du har en konfigurasjonsfil.

For å komme til vårt første grønne trinn, trenger vi bare å lage fixturen:

# fixtures / config.php 

Deretter har vi en Og trinn, som i dette tilfellet bare er et alias for gitt. Vi vil sørge for at konfigurasjonsfilen inneholder et alternativ for tidszonen. Igjen, dette er bare relatert til vår fixtur, så vi bryr oss ikke mye om det. Jeg slengte følgende (hackish) kode sammen for å oppnå dette:

/ ** * @Given alternativet: alternativet er konfigurert til: verdi * / offentlig funksjon theOptionIsConfiguredTo ($ option, $ value) $ config = inkluderer 'fixtures / config.php'; hvis (! is_array ($ config)) $ config = []; $ config [$ option] = $ value; $ content = "

Ovennevnte kode er ikke pen, men den oppnår hva den trenger. Det lar oss manipulere vår fixtur fra vår funksjon. Hvis du kjører Behat, vil du se at den har lagt til "tidssone" alternativet til config.php ligaen:

 'UTC',); 

Nå er det på tide å få inn noen av de originale "Taylor-koden"! Trinnet Når jeg laster opp konfigurasjonsfilen vil bestå av kode som vi egentlig bryr oss om. Vi vil ta med noen av koden fra den rå tekstfilen fra tidligere, og sørg for at den kjører:

/ ** * @ Når jeg laster konfigurasjonsfilen * / offentlig funksjon iLoadTheConfigurationFile () $ this-> config = Config :: load ('fixtures / config.php'); // Taylor! 

Running Behat, selvfølgelig vil dette mislykkes, siden config finnes ikke ennå. La oss bringe phpspec til redning!

Design med Phpspec

Når vi kjører Behat, får vi en dødelig feil:

PHP Fatal feil: Klassen 'Config' ikke funnet ... 

Heldigvis har vi phpspec tilgjengelig, inkludert sine fantastiske kodegeneratorer:

$ vendor / bin / phpspec desc "Config" Spesifikasjon for Config opprettet i ... /spec/ConfigSpec.php. $ leverandør / bin / phpspec kjøre --format = pen Vil du at jeg skal lage "Config" for deg? y $ leverandør / bin / phpspec kjøre --format = pen Config 10 ✔ er initialiserbar 1 spesifikasjoner 1 eksempler (1 bestått) 7ms 

Med disse kommandoene opprettet phpspec følgende to filer for oss:

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

Dette fikk oss av med den første dødelige feilen, men Behat kjører fortsatt ikke:

PHP Fatal feil: Ring til udefinert metode Config :: load () i ... 

laste() kommer til å være en statisk metode og som sådan, er ikke lett spekket med phpspec. Av to grunner er dette OK, skjønt:

  1. Oppførselen til laste() Metoden kommer til å være veldig enkel. Hvis vi trenger mer kompleksitet senere, kan vi trekke ut logikk til små testbare metoder.
  2. Oppførselen, som for nå, er dekket godt nok av Behat. Hvis metoden ikke laster filen inn i en matrise riktig, vil Behat skje på oss.

Dette er en av de situasjonene hvor mange utviklere vil ramme veggen. De vil kaste bort phpspec og konkludere med at det suger og arbeider mot dem. Men se hvor fint Behat og phpspec utfyller hverandre her?

I stedet for å prøve å få 100% dekning med phpspec, la oss bare implementere en enkel laste() fungere og vær sikker på at den er dekket av Behat:

settings = array ();  statisk statisk funksjonslast ($ path) $ config = new static (); hvis (file_exists ($ path)) $ config-> settings = inkluderer $ path; returner $ config;  

Vi er ganske sikre på at våre konfigurasjonsalternativer nå er lastet inn. Hvis ikke, vil resten av trinnene mislykkes, og vi kan se på dette igjen.

Bygg funksjonen med Iteration

Tilbake til grønt med både Behat og phpspec, kan vi nå se på vårt neste trinn Da skulle jeg få 'UTC' som 'tidszone' alternativ.

/ ** * @Then skal jeg få: verdi som: alternativvalg * / offentlig funksjon iShouldGetAsOption ($ verdi, $ alternativ) $ actual = $ this-> config-> get ($ option); // Taylor! Hvis (! strcmp ($ verdi, $ actual) == 0) kaste ny unntak ("Forventet $ actual å være '$ option'.");  

I dette trinnet skriver vi mer av den koden vi ønsker vi hadde. Kjører, men vi vil se at vi ikke har en få() metode tilgjengelig:

PHP Fatal feil: Ring til udefinert metode Config :: get () in ... 

Det er på tide å gå tilbake til phpspec og sortere dette ut.

Testing accessors og mutators, AKA getters og setters, er nesten som den gamle kyllingen eller egg dillemma. Hvordan kan vi teste få() metode hvis vi ennå ikke har sett() metode og omvendt. Hvordan jeg pleier å gå om dette er å bare teste dem begge samtidig. Dette betyr at vi faktisk skal implementere funksjonaliteten for å angi et konfigurasjonsalternativ, selv om vi ikke nå dette scenariet enda.

Følgende eksempel skal gjøre:

funksjon it_gets_and_sets_a_configuration_option () $ this-> get ('foo') -> shouldReturn (null); $ this-> set ('foo', 'bar'); $ Dette-> får ( 'foo') -> shouldReturn ( 'bar');  

Først vil vi få phpspec generatorer hjelpe oss å komme i gang:

$ leverandør / bin / phpspec kjøre --format = penne Vil du at jeg skal opprette 'Config :: get ()' for deg? y $ leverandør / bin / phpspec kjøre --format = penne Vil du at jeg skal opprette 'Config :: set ()' for deg? y $ vendor / bin / phpspec kjøre --format = pen Config 10 ✔ er initialiserbar 15 ✘ får og angir et konfigurasjonsalternativ forventet "bar", men ble null ... 1 spesifikasjoner 2 eksempler (1 bestått, 1 mislyktes) 9ms 

La oss nå komme tilbake til grønt:

offentlig funksjon få ($ option) if (! isset ($ this-> settings [$ option])) returnere null; returnere $ this-> innstillinger [$ option];  offentlige funksjonssett ($ alternativ, $ verdi) $ this-> settings [$ option] = $ value;  

Og der går vi:

$ leverandør / bin / phpspec kjøre --format = pen Config 10 ✔ er initialiserbar 15 ✔ får og setter et konfigurasjonsalternativ 1 spesifikasjoner 2 eksempler (2 bestått) 9ms 

Det fikk oss langt. Kjører, vi ser at vi er godt inn i det andre scenariet nå. Deretter må vi implementere standardalternativet, siden få() er bare tilbake null akkurat nå.

Første trekk er lik det vi skrev tidligere. I stedet for å legge til alternativet til matrisen, vil vi unset den:

/ ** * @Given alternativet: alternativet er ikke konfigurert ennå * / offentlig funksjon theOptionIsNotYetConfigured ($ option) $ config = include 'fixtures / config.php'; hvis (! is_array ($ config)) $ config = []; unset ($ config [$ alternativ]); $ content = "

Dette er ikke pent. Jeg vet! Vi kan sikkert refactor det, siden vi gjentar oss selv, men det er ikke omfanget av denne opplæringen.

Det andre funksjonstrinnet ser også kjent ut, og koples og limes for det meste fra tidligere:

/ ** * @Then skal jeg få standardverdien: standard som: alternativvalg * / offentlig funksjon iShouldGetDefaultValueAsOption ($ default, $ option) $ actual = $ this-> config-> få ($ option, $ default); // Taylor! Hvis (! strcmp ($ default, $ actual) == 0) kaste ny unntak ("Forventet $ actual til å være '$ default'.");  

få() er tilbake null. La oss hoppe over til phpspec og skrive et eksempel for å løse dette:

funksjon 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');  

Først kontrollerer vi at vi får standardverdien dersom "alternativet" ikke er konfigurert ennå. For det andre kontrollerer vi at standardalternativet ikke overskriver et konfigurert alternativ.

Ved første øyekast kan phpspec virke som overkill i dette tilfellet, siden vi nesten tester det samme med Behat allerede. Jeg liker å bruke phpspec til å spesifisere kanten-tilfellene skjønt, som er slags underforstått i scenariet. Og også, kodene generatorer av phpspec er veldig bra. Jeg bruker dem til alt, og jeg finner meg selv jobber raskere når jeg bruker phpspec.

Nå bekrefter phpspec hva Behat allerede fortalt oss:

$ vendor / bin / phpspec kjøre --format = pen Config 10 ✔ er initialiserbar 15 ✔ får og angir et konfigurasjonsalternativ 24 ✘ får en standardverdi når alternativet ikke er satt til forventet "bar", men ble null ... 1 spesifiserer 3 eksempler ( 2 bestått, 1 mislyktes) 9ms 

For å komme tilbake til grønt, vil vi legge til en "tidlig retur" til få() metode:

offentlig funksjon få ($ option, $ defaultValue = null) if (! isset ($ this-> settings [$ option]) og! is_null ($ defaultValue)) returnere $ defaultValue; hvis (! isset ($ this-> settings [$ option]) returnerer null; returnere $ this-> innstillinger [$ option];  

Vi ser at phpspec er nå glad:

$ vendor / bin / phpspec kjøre --format = pen Config 10 ✔ er initialiserbar 15 ✔ får og angir et konfigurasjonsalternativ 24 ✔ får en standardverdi når alternativet ikke er satt 1 spesifikasjoner 3 eksemplarer (3 bestått) 9ms 

Og slik er Behat, og det er vi også.

Vi er ferdige med vårt andre scenario og har en igjen å gå. For det siste scenariet trenger vi bare å skrive trinndefinisjonen for Og jeg stiller opsjonen 'tidszone' til 'GMT' skritt:

/ ** * @ Når jeg stiller alternativet: alternativkonfigurasjon til: verdi * / offentlig funksjon iSetTheConfigurationOptionTo ($ option, $ value) $ this-> config-> sett ($ option, $ value); // Taylor!  

Siden vi allerede implementerte sett() metode, dette trinnet er allerede grønt:

$ vendor / bin / behat Feature: Konfigurasjonsfiler For å konfigurere applikasjonen min Som utvikler må jeg kunne lagre konfigurasjonsalternativer i en fil Scenario: Å få et konfigurert alternativ #funksjoner / config.feature: 6 Gitt det er en konfigurasjon fil # FeatureContext :: thereIsAConfigurationFile () Og alternativet 'tidszone' er konfigurert til 'UTC' # FeatureContext :: theOptionIsConfiguredTo () Når jeg laster konfigurasjonsfilen # FeatureContext :: iLoadTheConfigurationFile 'option # FeatureContext :: iShouldGetAsOption () Scenario: Å få et ikke-konfigurert alternativ med en standardverdi # funksjoner / config.feature: 12 Gitt det er en konfigurasjonsfil # FeatureContext :: thereIsAConfigurationFile () Og alternativet' tidszone 'er ikke men konfigurert # FeatureContext :: theOptionIsNotYetConfigured () Når jeg laster konfigurasjonsfilen # FeatureContext :: iLoadTheConfigurationFile () Da skal jeg få standardverdien 'CET' som 'tidszone' -alternativet # FeatureContext :: iShould GetDefaultValueAsOption () Scenario: Angi et konfigurasjonsalternativ # funksjoner / config.feature: 18 Gitt det er en konfigurasjonsfil # FeatureContext :: thereIsAConfigurationFile () Og alternativet 'tidszone' er konfigurert til 'UTC' # FeatureContext :: theOptionIsConfiguredTo () Når Jeg laster konfigurasjonsfilen # FeatureContext :: iLoadTheConfigurationFile () Og jeg stiller opsjonen 'tidszone' til 'GMT' # FeatureContext :: iSetTheConfigurationOptionTo () Da skal jeg få 'GMT' som 'tidszone' -alternativ # FeatureContext :: iShouldGetAsOption ) 3 scenarier (3 bestått) 13 trinn (13 bestått) 0m0.04s (8.92Mb) 

Wrap up

Alt er fint og grønt, så la oss få en rask innpakning og se hva vi har oppnådd.

Vi har effektivt beskrevet den eksterne oppførselen til en konfigurasjonsfillaster, først ved å bruke Taylors tilnærming, og deretter ved å bruke en tradisjonell BDD-tilnærming. Deretter har vi implementert funksjonen ved hjelp av phpspec for å designe og beskrive den interne oppførelsen. Eksemplet vi har jobbet med er ganske enkelt, men vi har dekket grunnleggende. Hvis vi trenger mer kompleksitet, kan vi bare utvide det vi allerede har. Ved å bruke BDD har vi minst tre alternativer:

  1. Hvis vi observerer en feil eller trenger å endre noen internals av vår programvare, kan vi beskrive det som bruker phpspec. Skriv et sviktende eksempel som viser feilen og skriv koden som er nødvendig for å komme til grønt.
  2. Hvis vi trenger å legge til en ny brukstilstand til det vi har, kan vi legge til et scenario for  config.feature. Vi kan deretter iterativt jobbe oss gjennom hvert trinn, ved hjelp av Behat og phpspec.
  3. Hvis vi trenger å implementere en ny funksjon, for eksempel å støtte YAML config-filer, kan vi skrive en helt ny funksjon og starte over, ved hjelp av tilnærmingen vi har brukt gjennom denne opplæringen.

Med dette grunnleggende oppsettet, har vi ingen unnskyldninger for ikke å skrive en feiltest eller spesifikasjon, før vi skriver vår kode. Det vi har bygget er nå dekket av tester, noe som vil gjøre det mye enklere å jobbe med det i fremtiden. Legg til at koden vår også er fullt dokumentert. De tiltenkte brukstilfellene er beskrevet i vanlig engelsk og de interne arbeidene er beskrevet i våre spesifikasjoner. Disse to tingene vil gjøre det til en bris for andre utviklere å forstå og jobbe med kodebase.

Det er mitt håp at denne opplæringen hjalp deg med å bedre forstå hvordan BDD kan brukes i en PHP-kontekst, med Behat og phpspec. Hvis du har noen spørsmål eller kommentarer, vennligst legg dem under i kommentarfeltet.

Takk for at du leser sammen!