Phillip Trelford's Array

POKE 36879,255

What makes a good feature file

Back in March, Gojko Adzic and David De Florinier ran an illuminating interactive session at the excellent Cukeup! conference entitled What makes a good feature file. A feature file is a file containing a set of scenarios specified in Gherkin for consumption by Cucumber or a derivative thereof. No really ;)

Gherkin

Gojko’s opening statement rang true for me:

People focus on the automation part of Cucumber initially. Then they write 100s and 100s of feature files, which usually aren’t as useful as they could be

Clearly writing good feature files is important. However writing good feature files, as any good documentation, takes time, effort and collaboration.

Here’s Gojko & David’s tips for what makes a good feature file taken from the slides:

  • About the business domain – not software design
  • In domain language
  • Specification – not a script
  • Easy to understand
  • Focused on a single thing
  • Precise and testable
  • Self-explanatory
  • Consistent
  • Easy to access

There’s a great section in The Secret Ninja Cumber Scrolls also titled:

 What makes a good feature file

Our story needn't end there though…


Feature file style

Smells Like Teen Spirit

With the content for a good feature file explained, it’s time to turn our attention to style.

Sometimes it’s easier to say what we don’t like than what we do. So in a similar fashion to Martin Fowler’s Refactoring book let’s categorize some feature file bad smells and then define ways to neutralize the odour.

Categories of bad smell in a feature file:

  • Long sentences
  • Repetitious sentences
  • Repetitive scenarios
  • A Never ending story
  • Commentary
  • And another thing


Long sentences

Doing time

Diagnosis: There’s no easier way to lose a reader’s attention than overtly verbose sentences. If you find yourself having to use horizontal scroll to read a line then it’s probably too long.

Treatments:



Repetitious sentences

Lines

Diagnosis: The words in consecutive sentences somehow line up and few words vary between them. Nothing works better for putting the reader to sleep.

Treatments:

  • Extract the parts that vary into a table


Repetitive scenarios

Repetition

Diagnosis: The sentences in consecutive scenarios look almost identical, bar a few words varying between them. Nothing works better for confusing the reader.

Treatments:



A Never ending story

Neverending story

Diagnosis: Just like the 80s film of the same name, the story goes on and on, until the reader’s eyes start to glaze over. As a rule of thumb if a paragraph is longer than say 7 sentences, then it’s too long, the same can be applied for scenarios.

Treatment:

  • Consider using a background
  • Break up the scenario
  • Break the scenario up with tables


Commentary

8-2

Diagnosis: The scenario is so hard to follow that it has been commented. Yes, the document needs documentation. Documentation is not code.

Treatment:

  • Start again


And Another Thing 

And another thing

Diagnosis: Just when you thought you’d reached the end… Given proceeds When which proceeds Then, but wait there’s another thing, another Given.

Treatments:

  • Try describing the initial state in a single line.
  • Split into multiple scenarios


Positive Example

How does a good feature file look then? Here’s an example from Gojko & David’s slides:


Feature: Free Delivery 
In order to increase customer loyalty VIP customers buying a lot of books should get free delivery 

Scenario outline: Free delivery 
Given the threshold for free delivery is 10 books 
And the customer is <type> with <books> in the cart 
When checking out 
Then free delivery is <free delivery> 

Examples 
| type   | books | free delivery | 
| VIP    |   9   | not offered   | 
| VIP    |  10   | offered       | 
| VIP    |  11   | offered       | 
| Normal |  10   | not offered   |



Collaboration

Drawing Collaboration

In Liz Keogh’s words from the article Step Away from the Tools:

Start by having conversations, engaging both testers and business stakeholders

If you can’t have those conversations, stop. You can’t do BDD



Summary

Feature files are documents, and good documents should be short and to the point, easy to read and not put the reader to sleep. The real secret to good documentation is collaboration and review.

Has anyone read your feature file? Try inserting a particularly fallacious or comical statement towards the end and see if anyone mentions it ;)

Do you have some bad smells to share?



Further reading

Functional Programming with F#

This month F# became the first functional programming language to hit the top 20 of the TIOBE Programming Community Index:

The recent rise in popularity of F# comes as no surprise. Apart from being a nicely designed language, F# is available in the latest version of Microsoft's Visual Studio (2010).

Functional programming is by no means a new concept in fact it has its roots in the Lambda Calculus developed in the 1930s. The first popular functional flavoured language Lisp was developed in the 1950s and ML, a language that F# owes much of its syntax to, was developed  in the 1970s.

Some claim that functional programming concepts appear unfamiliar, in fact they are all around us, in Javascript, C#’s LINQ and even spreadsheets.. If you’ve ever written formulas in a spreadsheet then you’re already familiar with some of the principles of functional programming. The key building blocks of a spreadsheet are cells containing functions and values.

Over on Developer Fusion there’s a tutorial that demonstrates some key functional concepts while actually building a spreadsheet application in a couple of hundred lines of F#:

The functioning spreadsheet

If you already have some F# or functional programming then why not give it a try, otherwise you might want to read on.

Functional Programming Basics

The following concepts are considered specific to Functional Programming:

  • Pure functions
  • First class and higher order functions
  • Pattern matching

Pure Functions

Pure functions are functions that are free from side effects. That is pure functions always return the same result when called with the same parameters. Formulas in a spreadsheet hold this property.

First class and higher order functions

Functions are said to be first-class when they can be treated as first class objects. So like objects, functions can be passed as arguments to other functions, returned as values, assigned to values or stored in data structures.

Higher order functions are functions that take functions as their arguments. For example the LINQ Select method is an extension method that takes the function selector as a parameter:

public static IEnumerable<TSource>

    Select<TSource, TResult>(

        this IEnumerable<TSource> source, Func<TSource, TResult> selector)


Higher order functions are often used with anonymous functions as arguments. In the following line of code an anonymous function is passed to the Select function:

var squares = (new[] { 1, 2, 3 }).Select(n => n * n);


Support for higher-order and anonymous functions has been available in C# since 3.0 and will be available in C++ in the C++0x standard. They are also an integral part of JavaScript.

Pattern Matching

Many C based languages including C# supports basic pattern matching in the form of switch statements, matching against integral value and string types. In F#, and other ML based languages, a match statement is used instead:

C#

F#

switch (day) {

case 0: return "Sunday";

case 1: return "Monday";

case 2: return "Tuesday";

case 3: return "Wednesday";

case 4: return "Thursday";

case 5: return "Friday";

case 6: return "Saturday";

default:

 throw new

ArgumentException("day");           

}

 

match day with

| 0 -> "Sunday"

| 1 -> "Monday"

| 2 -> "Tuesday"

| 3 -> "Wednesday"

| 4 -> "Thursday"

| 5 -> "Friday"

| 6 -> "Saturday"

| _ –>

invalidArg "day" "Invalid day"

 

 

Functional programming languages allow pattern matching on types too. The following C# and F# code snippets are equivalent:

C#

F#

abstract class Shape { }

class Line : Shape {

 public Point Pt1;

 public Point Pt2;

}

class Square : Shape {

 public Point Pt;

 public float Size;

}

void Draw(Graphics g,Pen pen,Shape shape)

{

 if (shape is Line)

 {

  var line = (Line)shape;

  g.DrawLin(pen,line.Pt1,line.Pt2);

}

 else if (shape is Square)

{

  var sq = (Square)shape;

  g.DrawRectangle(pen,

   sq.Pt.X, sq.Pt.Y,

   sq.Size, sq.Size);

 }

}

type Shape =

| Line of Point * Point

| Square of Point * float

 

 

 

 

 

 

let draw (g:Graphics,pen:Pen,shape)=

  match shape with

  | Line(pt1,pt2) ->

     g.DrawLine(pen,pt1,pt2)

  | Square(pt,size) ->

     g.DrawRectangle(pen,

      pt.X,pt.Y,size,size)

 

 

In the code above the shape type is defined using an F# discriminated union, also known as algebraic data types in other functional languages like Haskell and Miranda. You can think of the type Shape as defining an empty abstract base class, and Line and Square as classes that inherit from the base class, just like the C# code to the left.

Note: The ‘*’ character in “Line of Point * Point” means “and”, i.e. a Line of Point and Point.

See Tomas Petricek and I implement the code above in the following video:

 

Starting F#

If you have Visual Studio 2010 then you already have F# installed, just start a New Project:

image

If you’re on Windows and don’t already have Visual Studio, you can also download the Visual Studio 2010 Shell for free and then install F#.

If you’re on Linux or Mac then you can use F# with Mono with MonoDevelop or Emacs. Or you can even edit F# code in your browser at http://tryfsharp.org.

image

F# Interactive

You can try out your F# code immediately in Visual Studio using the F# interactive window. Just highlight the code and presss ALT+ENTER

image

F# Basics

F# is a statically typed functional programming language on .Net that aims to minimize the ceremony of coding. F# functions are first class so they don’t need to be defined inside a class and type inference means that most of the time the compiler will infer the types of objects for you.

Values

The let keyword is used to define values with type inference (like the var keyword in C#).

Values in F# are by default immutable (constant):

let alwaysTrue = true


Values must be marked explicitly as mutable if you want to be able to change them:

let mutable variable = 1


Functions

The following function uses a conditional expression, with its type is inferred as a function that takes a bool and returns a bool:

let Not value = if true then false else true


Pattern Matching

The Not function can also be written as a match expression:

let Not value =

    match value with

    | true -> false

    | false> true


This is the shorthand equivalent using the function keyword:

let Not = function true -> false | false -> true


Pattern matching can be also be used to match against tabular values:

let And p q =

    match p, q with

    | true,  true   -> true

    | true,  false  -> false

    | false, true   -> false

    | false, false  -> false


One of the nice things about F# pattern matching is the added type safety. If a case is missing then the compiler gives a warning.

Discriminated Unions

A discriminated union is a type whose values can be any one of a set of types. You can think of it being like an enum in C# where each item has a specified type.

Option Types

The Option type is a simple example of a discriminated union, and it’s one that’s built into F#:

type Option<'TValue> = Some of 'TValue | None


The Option type is used when an actual value might not exist in which case it is specified as None, otherwise it is Some value. In F# the Option type is preferred over the use of null, and this helps remove a whole class of bugs.

Simple Parsing

Parsing a Spreadsheet formula based on whether it starts with an equals (‘=’) sign:

let parseFormula (s:string) =

    if s.StartsWith "=" then Some(s.Substring(1))

    else None


Parsing a decimal string value:

let parseNumber s =

    match Decimal.TryParse(s) with

    | true, n -> Some n

    | false, _ –> None


For a primitive spreadsheet we could define cell values as being either formulas or numbers:

type value = Formula of string | Number of decimal

Now we can match a text string with the parsers for formulas and numbers:

let parse s =

    match parseFormula s, parseNumber s with

    | Some formula, None -> Formula formula

    | None, Some number  -> Number number

    | _, _ -> invalidOp "Unknown value type"


Active Patterns

Active Patterns are defined inside banana brackets, i.e. ‘(|’, ‘|)’. An Active Pattern is a function that can be used actively inside a pattern matching expression.

The following Active Pattern returns whether a number is Odd or Even:

let (|Odd|Even|) n =

    if i % 2 = 1 then Odd else Even


The Active Pattern can then be used in a match expression:

match 1 with

| Odd -> "Odd"

| Even -> "Even"


A partial Active Pattern goes one step further and tries to match a specified value returning an Option of either Some value if successful, or None otherwise.

The following FormulaText partial Active Pattern can use the same body as the parseFormula function we defined earlier:

let (|FormulaText|_|) (s:string) =

   if s.StartsWith "=" then Some(s.Substring(1))

   else None


As the following NumericValue can use the same body as the earlier parseNumber function:

let (|NumberValue|_|) (s:string) =

    match Decimal.TryParse(s) with

    | true, n -> Some(n)

    | false, _ –> None


Now the Active Patterns may be used to simplify the earlier parse function:

let parse s =

    match s with

    | FormulaText text -> Formula text

    | NumberValue n -> Number n

    | _ -> invalidOp "Unknown value type"

 

What Next?

Time to put your new F# and functional programming skills to work and try creating your own Spreadsheet via the tutorial on Developer Fusion:

TickSpec: One Year On

It’s coming up to the first anniversary for TickSpec, a library and framework designed to support Behaviour Driven Development (BDD).

The first check-in was on the 30th August 2010:

Barebones BDD framework that supports a subset of the Gherkin language (Given,When,Then).

Since then the original aims of the project have been met, i.e create a lightweight parser library and invocation framework with first class support for F#, C# and integration with the major unit testing frameworks.

At the start of the project the focus was on core features, e.g.:

Added dynamic code generation so that you can step through feature files with the debugger

Added table support

Added Tags support

After the core features came a set of project examples for major unit testing frameworks including NUnit, xUnit, MbUnit and the Silverlight Unit Testing Framework.

TickSpecSLUT

Then with a number of projects using TickSpec in production, it was time to get out and about to talk with people about BDD and demonstrate how Acceptance Tests can be automated with TickSpec. Most recently a Live Meeting in June for the F# Community:


 

Over the last year I’ve been fortunate to attend and speak at quite a few related conferences and community events:

And there’s more on the horizon:

    OSJCukeyFSharp

And there are plenty more related articles here on this blog:

Thanks to recent user feedback on TickSpec there is also now a New Release with:

  • 100x Performance Increase (thanks Jon Harrop)
  • Improved error messages (thanks Martin Trojer)
  • A better C# debug experience (thanks James Banton)

In summary, a year on TickSpec has become a mature open source library and framework for automating acceptance tests on the .Net framework.


Disclaimer

Please bare in mind that a BDD framework is a means to an end and not the end in itself.

In the words of Gojko Adzic from his excellent new book Specification By Example (chapter 4, pages 46-47):

Don’t make test automation the end goal

this approach often leads to tests that are too technical

Don’t focus on a tool

this approach often ends up with programmers using a non-technical tool, intended for executable specifications, to manage technical, developer-orientated tests. This is a waste of time.

And Liz Keogh’s wise words in the article Step Away from the Tools:

BDD isn’t about the tools.

You don’t need BDD tools to do BDD.

Start by having conversations, engaging both testers and business stakeholders

If you can’t have those conversations, stop. You can’t do BDD

BuyerUser Beware!