Phil Trelford's Array
POKE 36879, 255

Method Stubs

November 25, 2011 00:34 by phil

I’ve spent the last 2-days of this week on a hands on TDD & Refactoring workshop ran by Jason Gorman. The course comprises of short exercises that you tackle in pairs. The provided machines came with Visual Studio 2010 installed so for most of the exercises my programming pair, Martin Trojer and I, used Visual F#.

One of the exercises was to implement a fictional holiday booking system that would connect to an external flight booking system and an external villa booking system. Holidays for a particular week of the year may only be booked if flights are available and one or more villas. The idea for the exercise was to test the holiday booking system using stubs for the flight and villa booking systems.

Being a Test Driven Development (TDD) course we Test First and Assert First, i.e. we write the test and the assertion before the code to implement the test:

[<Test>]
let ``when no flights are available then no holidays are available`` () =
    // ...
    Assert.AreEqual(holidays, [])

Another practice in XP/TDD is to write the simplest code that will make the test pass. In this case the simplest thing seemed to be to implement a function that gets the available holidays for a specific week given functions that determine flight and villa availability:

[<Test>]
let ``when no flights are available then no holidays are available`` () =
    let holidays = 
        getAvailableHolidays (isFlightAvailable, getAvailableVillas) (2,2013)
    Assert.AreEqual(holidays, [])

Now we implement the functions isFlightAvailable and getAvailableVillas as stubs:

[<Test>]
let ``when no flights are available then no holidays are available`` () =
    let isFlightAvailable _ = false
    let getAvailableVillas _ = ["Mountain Dew"]
    let holidays = 
        getAvailableHolidays (isFlightAvailable, getAvailableVillas) (2,2013)
    Assert.AreEqual(holidays, [])

With the stubs in place we can make the test fail:

let getAvailableHolidays (isFlightAvailable, getAvailableVillas) (week,year) = 
   getAvailableVillas (week,year)

The test fails because we are not checking flight availability. To make the test pass:

let getAvailableHolidays (isFlightAvailable, getAvailableVillas) (week,year) = 
   if isFlightAvailable (week,year) then getAvailableVillas (week,year)
   else []

Our first test passed. Time to refactor. In our test there is some code duplication, the stub functions isFlightAvaialble and getAvailableVillas always return the same value. We can create a method to encapsulate this:

let inline always value _ = value

Then we can use partial application to define a function getHolidaysWhenNoFlights:

[<Test>]
let ``when no flights are available then no holidays are available`` () =
    let getHolidaysWhenNoFlights = 
        getAvailableHolidays (always false, always ["Mountain Dew"])
    let holidays = getHolidaysWhenNoFlights (10,2012)
    Assert.AreEqual(holidays, [])

On to the next test case (which unfortunately passes immediately as the system has now been implemented):

[<Test>]
let ``when flights are available but no villas then no holidays`` () =
    let getHolidaysWhenNoVillas = 
        getAvailableHolidays (always true, always [])
    let holidays = getHolidaysWhenNoVillas (10,2012)
    Assert.AreEqual(holidays, [])

And so on:

[<Test>]
let ``flights and a villa are available then a holiday is available`` () =
    let getHolidays = 
        getAvailableHolidays (always true, always ["Mountain Dew"])
    let holidays = getHolidays (10,2012)
    Assert.AreEqual(holidays.[0], "Mountain Dew")

In F# I find myself starting with functions and then moving to classes if necessary. In C# and Java it seems people tend to start with classes, perhaps because refactoring from a static method to a class is felt to be hard. In F# it’s almost as easy as changing the let keyword to type, and declaring a member. So starting with our function to get available holidays:

let getAvailableHolidays (isFlightAvailable, getAvailableVillas) (week,year) = 
   if isFlightAvailable (week,year) then getAvailableVillas (week,year)
   else []

Refactored to a class:

type AvailableHolidays (isFlightAvailable, getAvailableVillas) = 
    member this.Get(week,year) =
        if isFlightAvailable (week,year) then getAvailableVillas (week,year)
        else []

Just before we ran out of time for this exercise we came up with a simple implementation for spying on method calls:

type Recorder () =
    let mutable xs = []
    member recorder.Record(x) = xs <- box x :: xs
    member recorder.Values = xs

let inline spy (recorder:Recorder) f = fun ps -> recorder.Record(ps); f ps

Which allowed us to check if a the flight booking system is being called with the correct values:

[<Test>]
let ``the flight availability system is passed the correct date`` () =
    let recorder = Recorder ()
    let isFlightAvailableSpy = spy recorder (always true)
    let getHolidays = 
        getAvailableHolidays (isFlightAvailableSpy, always [])
    let holidays = getHolidays (12,2012)
    Assert.AreEqual(recorder.Values,[12,2012])

So no frameworks required for either stubs or spies! .

Overall I felt a function based approach to TDD using F# worked very well, and resulted in a very simple solution.


Tags:
Categories: F# | .Net | Software Craftsmanship
Actions: E-mail | Permalink | Comments (5) | Comment RSSRSS comment feed

Comments

November 25. 2011 01:58

Carsten

nice post - I kind of like the function-first approach. Sadly most of the cases you normaly test (public methods) will be for some kind of framework - so have to fit into the .net environment. This is why I normaly do my tests in C# instead.

Another sad story is, that MOQ is just not working with F# in a nice way (syntax for expression-trees is just a deal-breaker) but you can use Rhino.Mocks and I would suggest doing so - it's true you can make stubs/mocks more easily in F# (inline-interfaces, ...) but if you use external dependencies like Repositories (till F# 3.0 I don't really want to do DB-access stuff in F# ;) ) you still have to implement each member of interfaces and Mock-frameworks really helps there too.

Carsten

November 25. 2011 12:02

Mathias

Nice post. My TDD experience with F# is much more limited than C#, but one thing which did strike me is that compared to C#, I barely need a Mocking framework, because of function composition. Instead of having to create on-the-fly a stub that implements a certain interface and replace the method, simply create a test method in the test and pipe it in, and it works...

Mathias

November 25. 2011 14:42

Phil

Thanks Carsten, Mathias,

Agreed, object expressions are good for mocking very small interfaces (like IDisposable), but don't scale as each test must implement all of the interface's members.
NaturalSpec, a testing framework written by Steffen Forkmann, also offers type mocking and provides F# quotation support.
Project: https://github.com/forki/NaturalSpec
Example: github.com/.../DealerMockingSample.fs
Source: github.com/.../Mock.fs

Phil

December 4. 2011 20:48

trackback

Seams, Mocks, and Functions

Seams, Mocks, and Functions

Clear Lines Blog

November 21. 2012 14:26

trackback

F# as a Unit Testing Language

F# as a Unit Testing Language

Phil Trelford's Array

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading