Programmeurs van vandaag gebruiken steeds meer agile praktijken in hun dagelijkse werk. Zelfs projecten die de standaard levenscyclus voor softwareontwikkeling volgen, kunnen er baat bij hebben om ze aan te passen. Automatisch testen en TDD brachten meer vertrouwen in ons werk, vergemakkelijkten het doorvoeren van aanpassingen aan bestaande functies en leidden vaak tot een beter codeontwerp. Maar nu is het niet genoeg. We moeten de voordelen van testen tot het uiterste drijven en BDD maakt dat mogelijk. BDD bouwt voort op TDD en voegt daar veel waarde aan toe. Het brengt alomtegenwoordige taal naar het project, zorgt voor betere communicatie tussen klant en ontwikkelaars. Het biedt veel voor projectmanagers en leiders, maar maakt ook het leven van een ontwikkelaar een stuk eenvoudiger. Het volgen van BDD-principes geeft ons duidelijke requirements, tests zijn gemakkelijker te begrijpen en ze kunnen dienen als documentatie. BDD verlegt de focus van testonderwerpen en geeft ons het vertrouwen dat we testen wat we moeten testen - gedrag.
Als je TDD gebruikt, is het eenvoudig om met BDD te beginnen - het is in feite een verzameling best practices hiervoor. BDD heeft een set eenvoudige regels die vertellen hoe je specificaties moet schrijven en wat je moet testen. Specificaties zijn verdeeld in drie delen: Gegeven (testvoorwaarden opstellen), Wanneer (actie op onderwerp uitvoeren) en Dan (asserties). Tests moeten beschrijvende namen hebben en bestaande testframeworks maken het mogelijk om methoden en asserties te gebruiken die lijken op natuurlijke taal - dit alles gecombineerd geeft ons tests die leesbaar zijn voor zowel technische als niet-technische gebruikers. Goede naamgevingsconventies komen van pas bij regressietests.
BDD komt ook met een set richtlijnen voor testonderwerpen. In tegenstelling tot TDD verschuift de focus van het testen van implementatie naar het testen van gedrag - en het gebruik daarvan leidt tot een beter ontwerp en geeft meer flexibiliteit als er veranderingen moeten worden doorgevoerd. Specs moeten zijn als een geschreven en uitvoerbare client requirements - de high level specificaties moeten fungeren als acceptatietests. Het doel is om tests zo te schrijven dat ze alleen hoeven te worden gewijzigd als de eisen veranderen. Het gebruik van BDD geeft ons het vertrouwen dat we testen wat echt getest moet worden en het is een meer pragmatische aanpak dan TDD.
Als je BDD in actie wilt zien, raden we Ruby aan. Het is een krachtige en leuke taal om te gebruiken en het heeft een uitstekende BDD toolset.
Komkommer
Cucumber is het populairste framework voor BDD in Ruby. Het introduceert een speciale taal, Gherkin genaamd, waarin je je tests schrijft. In tegenstelling tot RSpec, zijn functies beschreven in Gherkin platte tekst, niet codeen als zodanig door iedereen kan - en moet - worden begrepen, met name door de klant.
De functie van Cucumber ziet er als volgt uit (voorbeeld overgenomen van Cucumber wiki):
Functie: Beknopte maar beschrijvende tekst van wat gewenst is
Tekstuele beschrijving van de bedrijfswaarde van dit kenmerk
Bedrijfsregels die de reikwijdte van het kenmerk bepalen
Eventuele aanvullende informatie die de functie begrijpelijker maakt
Scenario: Een bepaalbare bedrijfssituatie
Gegeven een randvoorwaarde
En een andere voorwaarde
Wanneer een actie door de actor
En een andere actie
En nog een andere actie
Dan wordt er een testbaar resultaat bereikt
En er gebeurt nog iets anders dat we kunnen controleren
Scenario: Een andere situatie
...
Functies worden later in detail beschreven.
Komkommer gebruiken in Ruby on Rails
Installatie
De eerste stap om komkommer te gebruiken in je project installeert. Voeg gewoon deze twee edelstenen toe aan je Gemfile:
groep :test do
gem 'cucumber-rails', :require => false
gem 'database_cleaner
einde
Bundel ze door bundel installerenen genereer Cucumber scripts en directories met het volgende commando:
rails genereert komkommer:installeren
Dit creëert config/cucumber.yml, script/komkommer en functies/mapdie het volgende zal bevatten .functie bestanden, evenals stapdefinities en ondersteunende bestanden. Om je functies uit te voeren, gebruik je een nieuw rake-commando:
komkommer harken
Kenmerken
Laten we functies eens nader bekijken. Een functie is iets dat je applicatie heeft of doet - bijvoorbeeld periodiek een nieuwsbrief versturen of gebruikers toestaan om hun foto's publiekelijk te delen. Een functie bestaat uit meerdere scenario's, die beschrijven hoe deze functie werkt in verschillende contexten.
Om het basisgebruik van Cucumber in Rails te demonstreren, schrijven we een functie vanaf nul. Deze voorbeeldfunctie zal de eerste zijn die we schrijven in de applicatie, die zal worden getoond in het volgende tweede deel van dit artikel. Met deze applicatie kan de gebruiker artikelen en winkels maken (winkels verkopen artikelen) en vervolgens boodschappenlijsten maken.
In de eenvoudigste versie toont de applicatie na het samenstellen van het boodschappenlijstje in welke winkels de door de gebruiker gewenste artikelen het goedkoopst zijn.
Maak eerst een nieuw bestand met de naam maak_item.eigenschap in de functies/ map. Bovenaan een .functie bestand is er een Functie trefwoord, gevolgd door een korte beschrijving ervan. Bijvoorbeeld:
Functie: Items maken
In de volgende regels kun je een bedrijfsdoel schrijven dat deze functie zal implementeren. Aangezien Cucumber tekst negeert die vóór het eerste scenario is geschreven, is het niet nodig om iets te schrijven, maar het is zeker een goed idee.
Om gemakkelijk boodschappenlijstjes te maken met artikelen die in winkels in de buurt worden verkocht
Als gebruiker
Ik wil artikelen toevoegen aan het systeem
In het bovenstaande fragment zie je een vrij populair patroon waarmee je bedrijfsdoelen kunt beschrijven. Het bestaat uit drie delen: waarom is de functie nodig, wie wil het en wat is het. Natuurlijk hoef je dit format niet te volgen, maar zorg ervoor dat je antwoorden op deze drie vragen opneemt in je beschrijving.
Scenario's
Vervolgens schrijven we een aantal scenario's. Elk scenario begint met het sleutelwoord "Scenario" en bevat meerdere stappen.
Scenario: uniek item maken
Er is een item "Melk
Als ik naar de hoofdpagina ga
En ik maak een "Brood" item
Dan zie ik "Brood" in de Item-lijst
Dus als iemand een Item met de naam Melk heeft aangemaakt, dan is het mogelijk om Brood aan te maken en verschijnt Brood in de Item-lijst. We moeten niet testen op het maken van een record in de database, omdat dit feit geen echte waarde heeft voor de klant.
Scenario's stappen gebruiken drie belangrijke sleutelwoorden:
Gegevenvoor het leveren van context aan het scenario
Wanneervoor het beschrijven van acties
Danvoor het beschrijven van resultaten
Het is belangrijk op te merken dat Cucumber het trefwoord stap waarmee het begint negeert en alleen het latere deel matcht. Je kunt je stappen dus beginnen met "En" waar dat natuurlijk aanvoelt. De enige regel voor het kiezen van het juiste sleutelwoord is dat het Scenario gemakkelijk te begrijpen moet zijn.
Stap definities
Het uitvoeren van Cucumber levert nu deze uitvoer op:
[...]
Feature: Artikelen aanmaken
Om eenvoudig boodschappenlijstjes aan te maken met artikelen die in nabijgelegen winkels worden verkocht
Als gebruiker
Ik wil artikelen toevoegen aan het systeem
Scenario: uniek artikel aanmaken # kenmerken/creëer_artikel.kenmerk:6
Gegeven er is een "Melk" item # features/create_item.feature:7
Ongedefinieerde stap: "er is een "Milk" Item" (Cucumber::Undefined)
[...]
Dit komt omdat Cucumber niet weet wat we bedoelen als we zeggen "er is een Milk Item". Om betekenis toe te voegen aan je stappen moet je ze definiëren in stap_definities map.
Een aanbevolen manier om je stapdefinities te organiseren is om ze in te delen per domein. De meeste stappen die we hebben gemaakt, behoren bijvoorbeeld tot het domein Item. Dus moeten we een bestand maken met de naam stap_definities/item_steps.rb en plaats daar de volgende code:
Gegeven "er is een "$name" item" doe |name|
Fabriceren :item, naam: naam
einde
Als "ik een "$name" item maak" do |name|
binnen "#new_item" do
vul_in "Naam", met: naam
klik_op "Maken
einde
einde
Dan "Ik zie "$name" in de itemlijst" do |name|
binnen ".items" doen
expect(page).to have_content naam
einde
einde
De stappendefinities zijn meestal gebaseerd op Capybara. Als je niet bekend bent met Capybara, bekijk dan de links aan het einde van dit artikel.
Er is nog een stap die gedefinieerd moet worden en die niet echt binnen Item steps past. Je zou het in hoofdpagina_stappen.rb:
Wanneer "Ik ga naar de hoofdpagina" doe
bezoek root_pad
einde
Conclusie
Dit is een heel eenvoudig verhaal voor een heel eenvoudige functie. Het doel was om de kernconcepten van BDD te demonstreren zoals ze worden gebruikt in Cucumber. Het schrijven van goede stories vereist echter iets meer. Als ontwikkelaar moet je je mindset volledig veranderen - vergeet de implementatie en concentreer je in plaats daarvan op bedrijfsdoelen. Cucumber maakt deze overgang gemakkelijker door het slimme gebruik van platte tekstverhalen.