This is a story that has been about a year in the making. It has now reached a point where I think the story needs to be told to a wider audience.
At last year’s Citcon in Paris there was a presentation by Andy Palmer and Antony Marcano. They showed off a way of writing tests in Fitnesse that read much more nicely than you normally encounter. The underlying system they called JNarrate allowed writing tests in Java in a similarly readable fashion.
At youDevise we had been trying to figure out what framework we should be using to try to make things easier to understand and nicer to write. We had taken a look at Cucumber as well as a few other frameworks but none had really made us take the plunge yet. During the conference Steve Freeman mentioned that you don’t need to find a framework. You can instead evolve one by constantly refactoring, removing duplication, and making the code expressive.
Back at work I decided to try to give it a go. Guided by the small bit of JNarrate that I had seen at the conference I started trying to figure out what it would look like. The first passes at it (which got very quickly superseded) tried to do away with some of the things in JNarrate that didn’t seem very necessary. It turned out that many of them were. Not for the code to run, but for the code to be readable.
Eventually it turned into something that looked good.
Given.the(user).wasAbleTo(createAFundOfFundsNamed("FoHF") .ofCurrency(usd) .with(aSubFundNamed("SubFund").ofCurrency(usd))); When.the(user).attemptsTo(enterACapitalInflowInto("SubFund") .forTheAmountOf(300) .atAPriceOf(2)); Then.the(user).expectsThat(theCalculated(INFLOW_OUTFLOW)) .should(be(money(300, usd)));
We worked with this for a while and had very few changes to the basic structure of:
Given.the( <actor> ).wasAbleTo( <action> ); When.the( <actor> ).attemptsTo( <action> ); Then.the( <actor>).exptectsThat( <thing> ) .should( <hamcrest matcher> );
The framework itself is really just a few interfaces (Action, Actor, and Extractor) with the Given, When and Then classes being the only part of the framework that actually does anything. The rest of what is written is entirely part of the implementing application.
This style of writing non-unit tests has come to pervade large portions of our tests. It immediately helped immensely in communicating with business people and helping developers to understand the domain better. Since it was in Java we had the full support of the IDE for writing the tests and for refactoring them as our understanding of the scenarios improved. Once you get over the initial hurdle of defining the vocabulary, writing up new scenarios becomes so easy that we have started to sometimes go a little overboard with them 🙂
The only change that has occurred recently is that we dropped the standard Java camel-casing of identifiers and replaced them with underscores. We reached this decision after discovering that most of the pain of reading some of our more complex scenarios was in trying to parse the identifiers into sentences. SomeTimesItJustGetsALittleTooHardToFigureOutWhereTheIndividualWordsAre.
So a recent example is:
Given.the( company_admin) .was_able_to( modify_the_portfolio( the_portfolio) .to_add( author_in_other_company_who_published)); Given.the( author_in_other_company_who_published) .was_able_to( publish_an_idea() .with( a_long_recommendation_for( any_stock())) .to( a_portfolio_named( the_portfolio))); Given.the( company_admin) .was_able_to( modify_the_portfolio( the_portfolio) .to_remove( author_in_other_company_who_published)); When.the( company_admin).attempts_to(view_the_available_authors()); Then.the( company_admin) .expects_that(the_available_authors()) .should( have( author_in_other_company_who_published));