En agurktest


onsdag 24. februar 2010 Ruby Testing BDD

Om du ikke har levd under en stein det siste året så har sansynligvis hørt om Aslak Hellesøys Cucumber.

"Cucumber lets software development teams describe how software should behave in plain text. The text is written in a business-readable domain-specific language and serves as documentation, automated tests and development-aid - all rolled into one format."

"Cucumber works with Ruby, Java, .NET, Flex or web applications written in any language. It has been translated to over 30 spoken languages."

Men du har kanskje ikke testet det selv? Jeg tenkte derfor jeg kunne gjøre en rask demonstrasjon ved å skrive en akseptansetest for tjenesten jeg lagde i blogpostene om en minimal http-server i .Net / Ruby. Normalt vil man selvsagt beskrive featurene først, og så implementere dem og bruke cucumber til å bevise at de fungerer, men for enkelhets skyld tester jeg her altså min eksisterende tjeneste. Det spiller forøvrig ingen rolle om jeg tester .Net-varianten eller Ruby-varianten – jeg vil la akseptansetesten aksessere tjenesten direkte over http, og da er jo grensesnittet uansett det samme.

Det første jeg må gjøre er å beskrive tjenesten i form av en feature med tilhørende scenarier. Virker dette litt gresk anbefaler jeg at du leser deg litt opp på Behaviour-Driven Development (BDD).

File: features\add_service.feature:
1 Feature: Add-service
2   In order to save cycles on client CPUs
3   As an IT manager
4   I want a central server that can add numbers
5
6   Scenario: Add a string of numbers
7     When I send 9,11,33,100 to the add service
8     Then the response should be "The answer is 153"
9
10   Scenario: Add a single number
11     When I send 1 to the add service
12     Then the response should be "The answer is 1"

Feature-filen er input til cucumber, som vil analysere stegene i scenariene og forsøke å eksekvere dem. Men foreløpig skjønner cucumber lite, så vi må skrive litt kode for å definere stegene.

File: features\add_service_steps.rb:
1 require 'open-uri'
2 require 'spec/expectations'
3
4 When /I send (.*) to the (.*) service/ do |argument, service|
5   open("http://127.0.0.1:4567/#{service}?#{argument}") do |response|
6     @data = response.read
7   end
8 end
9
10 Then /the response should be "([^\"]*)"/ do |expected|
11   @data.should == expected
12 end

Sånn, det er alt som trengs. Jeg lager et "When I send X to the Y service"-steg som gjør en forespørsel til tjenesten min og sender de definerte argumentene. Cucumber tar seg av å bytte ut X og Y med argumentene og navnet på tjenesten. "Then the response should be Z"-steget gjør en assert mot det forventede svaret som er beskrevet i scenariene.

Legg merke til at det er to forskjellige DSL'er (domenespesifike språk) i bruk her – en ekstern DSL for å definere features, og en intern Ruby-DSL (intern som i "er gyldig ruby-kode) for å definere stegene.

Når jeg kjører cucumber nå får jeg følgende output:

C:\Users\tormar\ruby_projects\httpListener>cucumber
Feature: Add-service
        In order to save cycles on client CPUs
        As an IT manager
        I want a central server that can add numbers

  Scenario: Add a string of numbers
    When I send 9,11,33,100 to the add service
    Then the response should be "The answer is 153"

  Scenario: Add a single number
    When I send 1 to the add service
    Then the response should be "The answer is 1"

2 scenarios (2 passed)
4 steps (4 passed)
0m0.122s

Alle scenariene for Add-servicen passerte (hvis ikke hadde det dukket opp endel rødt i output). Kult?

Hvis du er alergisk mot Ruby så finnes det også et prosjekt som heter SpecFlow som er en port av cucumber til .Net. Men det er absolutt å anbefale å benytte the real thing, gjerne med IronRuby på .Net-plattformen.


comments powered by Disqus