Project Description
FsStory is a library for writing executable user stories in F#.

NB. Publishing this project just to avoid CodePlex removing it. Do not use.

FsStory enables the developer to write user story scenarios (in Given/When/Then form) in F# code, like this:

#light

open FsStoryRunner
open MutatedTurtleMovesImpl
open Xunit

(* 
In order to impress my friends
As a .NET programmer
I want to draw funny fractal pictures
*)

[<Fact>]
let MoveTurtleToPosition() = 
       given ATurtle
    |> andGiven IsRotated90DegreesToTheRight
    |> whens (TurtleWalksSteps 9)
    |> thens (TurtleIsLocatedAt (0,9))   
    |> endStory

Did you notice [<Fact>] attribute just before the function definition? It is a xUnit.net specific attribute, telling xUnit.net that the function is a runnable test. So, why xUnit.net? Answer: xUnit.net is the currently the only test framework that runs static test methods (that is, in IL), which is what F# functions compiles to.

Note: Some people may think that the story above is too low level to be a "good" user story. We think they're right, but it's just an example. Please contribute to the project with more examples.

The "ATurtle", "IsRotated90DegreesToTheRight", "TurtleWalksSteps", etc, are functions that you have to implement yourself. What these functions do is not FsStory's business, except that they have the same type. It's a good thing to think about this in advance.

If you're testing something object oriented, i.e. a C# project, then you're probably have to let the functions have the type () -> (). That is, they take no argument and return void, in C# lingo. You'd also need a mutable variable to accomplish this.

#light

open Turtle
open FsxUnit.Syntax

let mutable turtle = new Turtle() // turtle must have type Turtle

let ATurtle () = turtle <- new Turtle() // reuse function for setup of new turtle in same story
let MovesOneStepForward () = turtle.Go()
let IsMovedOneStepForward () = turtle.Position.X |> should equal 1
let RotationIs angle () = turtle.Direction |> should equal 0.0

Another style is to work with immutable objects. One example of immutable objects are value objects, in DDD. Immutable objects correspond well to functional programming principles. Here is an example of an implementation of a scenario when an immutable object is used in the SUT (System Under Test).

let ATurtle () = new TurtleImmutable()
let IsRotated90DegreesToTheRight = fun (turtle : TurtleImmutable) -> turtle.Left()
let TurtleWalksSteps steps = fun (turtle : TurtleImmutable) -> turtle.GoSteps(steps)
let TurtleIsLocatedAt (x,y) = fun (turtle : TurtleImmutable) -> turtle.Position |> should equal (new Position(x,y)) ; turtle

To clarify, all methods on the (immutable) turtle return a new turtle and that turtle is returned and then passed in as an argument to the next test function (by FsStory). As you might have spotted, the example uses a lambda, an anonymous functions (the "fun") instead of specifying an argument explicitly. We have noticed that it's a good thing to get a running story before actually implementing the logic and assertions. Using the function "id" (just returning the argument) on the right-hand side is very helpful for getting everything to run.

It's also possible to split step definitions, to gain more fluent language:

[<Fact>] 
let tableLegsScenario =
     given (ATableWith 4 Legs)
  |> whens (ICutOf 1 Leg)
  |> thens (ItHasOnly 3 LegsLeft)

For more info on this, go to http://coffeedrivendevelopment.blogspot.com/2009/02/fluent-language-in-fsstory.html

Yet another style to specify tests is using a "state monad". Currently, using this style requires a small change in the story specification, adding brackets after the "endStory" and using the monadic runner. Examples of this is included in the source code.

To use FsStory, click the "Downloads" link to your right. But you probably want to download the source as well and read the examples.

Last edited Dec 19, 2012 at 3:11 PM by gustaf_nk, version 21