Nutidens programmører bruger flere og flere agile metoder i deres daglige arbejde. Selv projekter, der følger en standard livscyklus for softwareudvikling, kan have gavn af at tilpasse dem. Automatisk testning og TDD gav os mere selvtillid i vores arbejde, gjorde det lettere at implementere ændringer i eksisterende funktioner og førte os ofte til bedre kodedesign. Men nu er det ikke nok. Vi er nødt til at presse fordelene ved test til det yderste, og det giver BDD mulighed for. BDD bygger oven på TDD og tilføjer en masse værdi til dens praksis. Det bringer et allestedsnærværende sprog ind i projektet og giver mulighed for bedre kommunikation mellem kunde og udviklere. Det giver meget til projektledere og chefer, men gør også livet meget lettere for en udvikler. Ved at følge BDD-principperne får vi klare krav, testene er lettere at forstå, og de kan fungere som dokumentation. BDD flytter fokus for testpersonerne og giver os tillid til, at vi tester det, vi skal teste - adfærd.
Hvis du bruger TDD, er det nemt at starte med BDD - det er dybest set et sæt bedste praksisser for det. BDD har et sæt enkle regler, som fortæller, hvordan man skriver specifikationer, og hvad man skal teste. Specifikationer er opdelt i tre dele: Given (opsætning af testbetingelser), When (påkaldelse af handling på emnet) og Then (påstande). Test bør have beskrivende navne, og eksisterende testframeworks giver mulighed for at bruge metoder og assertions, der ligner naturligt sprog - alt dette kombineret giver os test, der er læsbare for både tekniske og ikke-tekniske brugere. Gode navngivningskonventioner viser sig nyttige under regressionstests.
BDD kommer også med et sæt retningslinjer for testpersoner. I modsætning til TDD flytter det fokus fra test af implementering til test af adfærd - og brugen af det fører til bedre design og giver mere fleksibilitet, når der skal indføres ændringer. Specifikationer skal være som skriftlige og eksekverbare kundekrav - dem på højt niveau skal fungere som accepttest. Målet er at skrive tests på en måde, så det kun er nødvendigt at ændre dem, når kravene ændres. At bruge BDD giver os tillid til, at vi tester det, der virkelig skal dækkes, og det er en mere pragmatisk tilgang end TDD.
Hvis du vil se BDD i aktion, anbefaler vi Ruby. Det er et stærkt sprog, som er sjovt at bruge, og det har et fremragende BDD-værktøjssæt.
Agurk
Cucumber er det mest populære framework til BDD i Ruby. Det introducerer et særligt sprog, kaldet Gherkin, som du kan skrive dine tests i. I modsætning til RSpec er funktioner beskrevet i Gherkin almindelig tekst, ikke Kodeog som sådan kan - og bør - forstås af alle, især klienten.
Cucumber-funktionen ser sådan ud (eksemplet er taget fra Cucumber-wiki):
Funktion: En kortfattet, men beskrivende tekst om, hvad der ønskes
Tekstlig beskrivelse af den forretningsmæssige værdi af denne funktion
Forretningsregler, der styrer funktionens omfang
Eventuelle yderligere oplysninger, der vil gøre funktionen lettere at forstå
Scenarie: En bestemt forretningssituation
Givet nogle forudsætninger
Og nogle andre forudsætninger
Når en eller anden handling fra aktørens side
Og en anden handling
Og endnu en handling
Så opnås et eller andet testbart resultat
Og der sker også noget andet, vi kan kontrollere
Et scenarie: En anderledes situation
...
Funktionerne vil blive beskrevet i detaljer senere.
Brug af agurk i Ruby on Rails
Installation
Første skridt til at bruge agurk i din projekt er at installere den. Du skal bare tilføje disse to gems til din Gemfile:
gruppe :test do
gem 'cucumber-rails', :require => false
gem 'database_cleaner'
slut
Saml dem ved at køre installation af bundtog generere Cucumber-scripts og -mapper med følgende kommando:
rails genererer cucumber:install
Dette vil skabe config/cucumber.yml, script/agurk og funktioner/ bibliotek, som vil indeholde .funktion filer samt trin-definitioner og understøttende filer. For at køre dine funktioner skal du bruge en ny rake-kommando:
rive agurk
Funktioner
Lad os se nærmere på funktioner. En funktion er noget, din applikation har eller gør - f.eks. sender den jævnligt nyhedsbreve eller giver brugerne mulighed for at dele deres fotos offentligt. En funktion består af flere scenarier, som beskriver, hvordan denne funktion fungerer i forskellige sammenhænge.
For at demonstrere den grundlæggende brug af Cucumber i Rails skriver vi en funktion fra bunden. Dette eksempel på en funktion vil være den første, vi skriver i applikationen, som vil blive vist i den kommende anden del af denne artikel. Denne applikation vil give brugeren mulighed for at oprette varer og butikker (butikker sælger varer) og derefter oprette indkøbslister.
I den enkleste version viser applikationen efter kompilering af indkøbslisten, i hvilke butikker de varer, som brugeren ønsker, er billigst.
Opret først en ny fil med navnet opret_artikel.funktion i funktioner/ bibliotek. I toppen af en .funktion fil er der en Funktion nøgleord, efterfulgt af en kort beskrivelse af det. For eksempel:
Funktion: Oprettelse af elementer
I de følgende linjer kan du skrive et forretningsmål, som vil implementere denne funktion. Da Cucumber ignorerer tekst, der er skrevet før det første scenarie, er det ikke nødvendigt at skrive noget, men det er bestemt en god idé.
For nemt at kunne oprette indkøbslister med varer, der sælges i nærliggende butikker
Som bruger
Jeg vil gerne tilføje varer til systemet
I uddraget ovenfor kan du se et ret populært mønster, som du kan bruge til at beskrive forretningsmål. Det består af tre dele: hvorfor er funktionen nødvendig, hvem vil have den, og hvad er den. Du behøver selvfølgelig ikke at følge dette format, men sørg for at inkludere svar på disse tre spørgsmål i din beskrivelse.
Scenarier
Dernæst skriver vi nogle scenarier. Hvert scenarie begynder med nøgleordet "Scenario" og indeholder flere trin.
Scenarie: oprettelse af unik vare
Givet at der er en "Mælk"-vare
Når jeg går til hovedsiden
Og jeg opretter varen "Brød"
Så ser jeg "Brød" på varelisten
Så hvis nogen har oprettet en vare ved navn Mælk, så er det muligt at oprette Brød, og det resulterer i, at Brød vises på varelisten. Vi bør ikke teste for oprettelse af en post i databasen, da dette faktum ikke har nogen reel værdi for kunden.
Scenarios trin bruger tre hovednøgleord:
Givet, for at give kontekst til scenariet
Nårtil at beskrive handlinger
Derefter, til beskrivelse af resultater
Det er vigtigt at bemærke, at Cucumber ignorerer det nøgleordstrin, det starter med, og kun matcher den senere del. Du kan derfor starte dine trin med "And", hvor det føles naturligt. Den eneste regel for at vælge det rigtige nøgleord er, at scenariet skal være let at forstå.
Definitioner af trin
Når man kører Cucumber, får man nu dette output:
[...]
Funktion: Oprettelse af varer
For nemt at kunne oprette indkøbslister med varer, der sælges i nærliggende butikker
Som bruger
Jeg vil gerne tilføje varer til systemet
Scenarie: Oprettelse af en unik vare # features/create_item.feature:6
Givet at der er en "Mælk"-vare # features/create_item.feature:7
Udefineret trin: "der er et "Milk"-element" (Cucumber::Undefined)
[...]
Det skyldes, at Cucumber ikke ved, hvad vi mener, når vi siger "der er et Milk Item". For at tilføje mening til dine trin skal du definere dem i trin_definitioner bibliotek.
En anbefalet måde at organisere dine trindefinitioner på er at opdele dem efter domæne. For eksempel hører de fleste af de trin, vi har oprettet, til Item-domænet. Derfor bør vi oprette en fil med navnet step_definitions/item_steps.rb og placer følgende kode der:
Givet "der er en "$name"-genstand" do |name|
Fremstil :genstand, navn: navn
slut
Når "jeg opretter en "$name"-genstand" do |name|
inden for "#new_item" do
fill_in "Navn", med: navn
klik_på "Opret"
slut
slut
Then "Jeg ser "$name" på varelisten" do |name|
inden for ".items" do
expect(page).to have_content navn
end
end
Definitionerne af trin vil normalt være baseret på Capybara. Hvis du ikke er bekendt med Capybara, så tjek linksene i slutningen af denne artikel.
Der er endnu et trin, der skal defineres, og det passer ikke rigtig ind i Item-trin. Du kunne placere det i main_page_steps.rb:
Når "Jeg går til hovedsiden" gør
besøg root_path
slut
Konklusion
Dette er en meget enkel historie om en meget enkel funktion. Formålet var at demonstrere kernebegreberne i BDD, som de bruges i Cucumber. At skrive gode historier kræver dog lidt mere. Som udvikler bliver du nødt til at ændre din tankegang fuldstændigt - glem alt om implementering og fokuser på forretningsmål i stedet. Cucumber gør denne overgang lettere med sin smarte brug af almindelige teksthistorier.