Fra enhentstester til kjørbare spesifikasjoner

Dan NorthDet er snart et år siden Dan North var i Bergen og snakket om Behaviour-Driven Development (BDD) – et besøk jeg bl.a. dokumenterte i the Contiki Strip #5. Det var et utrolig inspirerende møte, og Given-When-Then spesifikasjonformatet var noe vi prøvde å jobbe med i tiden etterpå. Men siden vi ikke hadde kommet orntlig igang med å skrive enhetstester, og var langt fra å praktisere TDD, glemte vi det raskt ut, og alt forble ved det gamle.

Nå som jeg har jobbet mer med testdreven utvikling er det på tide å ta dette opp igjen. Jeg vil derfor presentere hvordan man kan gå fra “vanlige” unit-tester til å implementere kjørbare spesifikasjoner (the BDD way), og snakke litt om hvilke fordeler dette gir oss.

Ta f.eks. denne enhetstesten, hentet fra Robert C. Martins bok Agile Principles, Patterns, and Practices in C#:

    1 [Test]

    2 public void DeleteEmployee()

    3 {

    4     int empId = 4;

    5     AddComissionedEmployee t =

    6         new AddComissionedEmployee(empId, “Bill”, “Home”, 2500, 3.2);

    7     t.Execute();

    8 

    9     Employee e = PayrollDatabase.GetEmployee(empId);

   10     Assert.IsNotNull(e);

   11     DeleteEmployeeTransaction dt = new DeleteEmployeeTransaction(empId);

   12     dt.Execute();

   13 

   14     e = PayrollDatabase.GetEmployee(empId);

   15     Assert.IsNull(e);

   16 }

Denne koden har utstract bruk av COMMAND pattern. Testen oppretter først en Employee i databasen (line 5-7), og bekrefter at den ble opprettet (line 9-10). Deretter sender den en slette-komando (linje 11-12) og verifiserer at den nå ikke finnes i databasen lengre (linje 14-15). En helt normal enhetstest etter min mening.

Den uttrykker derimot ikke så mye om intensjonen bak testen – man må lese koden for å forstå hva som skjer, og strukturen er helt og holdent opp til utvikleren. En bedre måte å gjøre dette på er å sette opp et Given-When-Then-senario: Gitt at vi har en kunde, Når vi ber om å få den slettet, finnes den ikke lengre i databasen.

Implementert vha. TinyBDD, et rammeverkt for å lage BDD-spesifikasjoner under utvikling av Gjøran Hansen, forvandles enhetstesten til noe sånn som dette:

    1 [Test]

    2 public void DeleteEmployeeTransaction_should_remove_employee()

    3 {

    4     int empId = 4;

    5 

    6     Scenario.New(“DeleteEmployeeTransaction should remove employee”,

    7         scenario =>

    8     {

    9         scenario.Given(“An Employee with a specific ID”, () =>

   10             new AddComissionedEmployee(empId, “Bill”, “Home”, 2500, 3.2).Execute())

   11 

   12                 .And(“It exists in the repository”, () =>

   13                     Assert.IsNotNull(PayrollDatabase.GetEmployee(empId)));

   14 

   15         scenario.When(“A delete transaction is sent with that ID”, () =>

   16             new DeleteEmployeeTransaction(empId).Execute());

   17 

   18         scenario.Then(“The Employee should no longer exist in repository”, () =>

   19             Assert.IsNull(PayrollDatabase.GetEmployee(empId)));

   20 

   21     }).Execute();       

   22 }

Om du ikke er vandt med lambda-uttrykk i C# er dette sikkert ganske gresk, men gi det en sjanse likevel. I linje 6 opprettes et nytt senarie. Så på linje 9 sier vi at hvis (Given) en bestemt Employee existerer, og (på linje 12) han finnes i databasen, når (When) man da eksekverer en slette-kommando (linje 15), så (Then) vil Employee’en ikke lenger finnes i basen (linje 18).

Lambda-uttrykkene på linje 10, 13, 16 og 19 utfører og/eller tester det vi har spesifisert i kallene til Given, When og Then. Og sammenligner du med den originale testen så ser du at de egentlig gjør akkurat det samme.

Forskjellen er at BDD-spesifikasjonen beskriver/dokumenterer i klartekst hva situasjonen er og hvilken adferd vi er ute etter. Og spesifikasjonen er så nær koden at det ikke vil være noe problem å holde dem og koden synkronisert om den skulle endres. Den er relativt oversiktelig, og følger et fast mønster.

Og den virkelige gevinsten oppdager du når du innser at disse spesifikasjonene kan skrives av eller i sammarbeid med testere, product owners og til og med kunden selv. Spranget fra kundens eller produktets krav til selve koden blir minimal. Utvikleren og kunden kan f.eks. sitte sammen og skrive spesifikasjonen rett inn i en .cs-fil.

Når spesifikasjonene er definert kan man så kjøre dem underveis i prosjektet, og ta ut rapporter som i klartekst beskriver senariene, og hvilke av dem som er ferdig implementert, hvilke som gjenstår, og eventuelt hvilke som feiler (TinyBDD støtter ikke dette enda såvidt meg bekjent).

BDD er mye mer enn det jeg har vist her. For en praktisk introduksjon til et annet BDD rammeverk som heter MSpec anbefaler jeg at du tar en titt på Rob Conery’s screencast om BDD – meget inspirerende. Og for mer informasjon om BDD generelt er behaviour-driven.org en bra ressurs. Det har vært mye forvirring rundt testdreven utvikling, og BDD gir en litt annen vinkling på problemet, og et sterkere fokus på adferd, noe som vil kunne øke kvaliteten på programvaren du produserer.

Knagger: , , ,

Kategorier: Softwareutvikling, Testing / TDD.
RSS feed for kommentarene. Tilbaketråkk.

2 kommentarer til “Fra enhentstester til kjørbare spesifikasjoner”

  1. Gøran Hansen Says:

    Hei Torbjørn

    Takk for at du nevnte TinyBDD i posten. Håper du fortsatt bruker det og er fornøyd med det:)? Jeg holder på å jobbe med noen oppdateringer som vil komme i løpet av en uke eller to.

    Gøran

  2. Torbjørn Says:

    Ja, jeg bruker TinyBDD aktivt. Rammeverket er en meget smertefri innfalsvinkel til BDD og bedre tester generelt, og jeg ser frem til nye oppdateringer. Keep up the excellent work!

Skriv en kommentar

Tillatte tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


Torbjørn: La oss anta to ulike definisjoner av Template Method pattern - de to ytterpunkte...

Lars-Petter: Hei igjen. Siden du inviterer til å ta diskusjonen i bloggen, og har tatt deg t...

Torbjørn: Lars-Petter: Det er én måte å se det på. Det er absolutt fortsatt Template M...

Lars-Petter: Hei. Har du ikke i prinsippet her gått over fra Template Method (arv) til Strat...

Christian Abildsø: I alle fall i C#, så føles dette som kode som blir mer fleksibel men vanskelig...

Torbjørn: Hei Henrik, og takk for tilbudet. Ble oppmerksom på Rasberry Pi for under en uk...

Henrik Sandaker Palm: Ang. større hobby prosjekt. Du er som er en slik rakker på programmering har j...

Øivind Nilsen: Slutt å bruke mobilnummeret mitt som eksempel !...

Bjørn Einar Bjartnes: Jeg har også latt meg fascinere av Clojure, uten at jeg har kommet så veldig l...

Bjørn Einar Bjartnes: Sweet :) Jeg tror egentlig jeg liker det som det er, med musikk. Litt av utford...

Mulig relaterte linker

 Hold deg oppdatert

Søk i bloggen

Ferske innlegg

  • Template Method del 4: Multippel arv
  • Template Method Intermesso
  • Template Method del 3: Bare funksjoner
  • Template Method del 2: På vei mot funksjonell programmering
  • Kategorier

  • .net ninja (37)
  • Bøker (17)
  • Diverse prosjekter (35)
  • DSL (10)
  • Erlang (10)
  • F# (5)
  • Hardware (1)
  • Jobb (77)
  • Julekalender (51)
  • kjempekjekt.com (23)
  • LISP/Clojure (33)
  • NNUG / community (60)
  • O/RM & databaser (10)
  • Off topic (116)
  • OO-design/clean code (30)
  • Podcasts (14)
  • Polyglot (77)
  • Ruby (27)
  • Silverlight / RIA (3)
  • Software/verktøy (20)
  • Softwareutvikling (20)
  • Testing / TDD (30)
  • the contiki strip (13)
  • User experience (3)
  • WCF (3)
  • Webutvikling (32)
  • WPF (9)
  • WTF (12)
  • Last ned Wallpaper

    Programmeringsbloggens tøffe skrivebordsbakgrunn med snippets fra ulike språk laster du ned her!

    Abonner via epost

    Om du vil kan du få alle nye blogposter tilsendt til din epost. Abonner nå, det er kjempeenkelt!

    Meta