April 2009 - Posts

Writing BDD-Style tests in F# with xUnit.net

Recently, I’ve been exploring different options for testing in F#. I was pleased to find out that there has been some good work done in this space already:

  • Matthew Podowysoki’s Functional Programming Unit Testing series is a great overview of testing in F#.
  • The FsUnit project is a specification style testing framework written in F#.
  • FsCheck is a port of Haskell’s QuickCheck to F#. QuickCheck is a tool for randomly generating tests.

One of the things that I didn’t find was a good solution for BDD-style testing. I’m not a BDD fanatic, but I am definitely a fan of the extra mile that BDD frameworks go to blend tests and specifications. FsUnit has support for specification style tests, but as a whole, I felt that the project was a too young to be an ideal solution. Plus, I didn’t have the desire to start learning and using yet another testing framework.

Using xUnit.net as a starting point, I decided to try writing my own BDD extension.

 

Let’s say that I have a two dimensional vector type that I want to test:

    type Vector = 
        { X : float;
          Y : float; }
    module VectorOperations =
        let Add one two =
            { X = one.X + two.X; Y = one.Y + two.Y }

 

The syntax that I came up with looks like this:

let context = Given "a zero vector, v1," {X=0.0; Y=0.0}

[<BDDFact>]
let VectorAdditon () =
   
let outcome = context.With "a vector, v2, of (1,1) added to it" (fun vector -> vector + {X=1.0; Y=1.0})
    outcome.Observe
"the result is a new vector = (v1.x + v2.x, v1.y+v2.y)." (fun _ result -> Assert.Equal({X=1.0; Y=1.0}, result))

This syntax is very similar to the syntax that NBehave uses, but I like that it does not rely on a combination of closure and mutable state to pass information from between the “given”, “with”, and “then/assert/observe” portions of the test. I also like that I don’t have to rely on class hierarchies and a bulky testing framework to get the BDD style naming that I want.

Here’s the full implementation:

#light

namespace BDDExtensions

open System
open System.Reflection
open System.Xml
open Xunit
open Xunit.Sdk

module BDDExtensions =
    let mutable Observations = []
    
    type Outcome<'a, 'b>(message, context, result) =
        member this.Observe observationMessage (action:'a -> 'b-> unit) =
            Observations <- (message, (fun () -> action context result)) :: Observations
            
    type Context<'a>(message:string, value) =
        member this.With withMessage func =
            new Outcome<'a, 'b>(String.Concat([|message; "with"; withMessage|]), value, func(value))
         
    let Given (message:string) (value:'a) = new Context<'a>(String.Concat("Given ", message), value)
    
    type BDDCommand(assertion, displayName, info) =
        interface ITestCommand with
            member this.ShouldCreateInstance = false
            member this.Execute testClass = 
                try
                    assertion()
                    new PassedResult(info, displayName) :> MethodResult
                with 
                    ex -> new FailedResult(info, ex, displayName) :> MethodResult
            member this.DisplayName = displayName
            member this.ToStartXml () =
                     let doc = new XmlDocument()
                     doc.LoadXml("<dummy/>")
                     let testNode = XmlUtility.AddElement(doc.ChildNodes.[0], "start")
                     XmlUtility.AddAttribute(testNode, "name", displayName)
                     XmlUtility.AddAttribute(testNode, "type", info.ReflectedType.FullName)
                     XmlUtility.AddAttribute(testNode, "method", info.Name)
                     testNode 
                     
    [<AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)>]
    type BDDFactAttribute() =
        inherit FactAttribute()
        override this.EnumerateTestCommands(info:MethodInfo) =
            info.Invoke(null, null) |> ignore
            Observations
            |> Seq.map (fun (name, assertion) -> new BDDCommand(assertion, name, info))
            |> Seq.cast<ITestCommand>

As displayed in the Vector example above, the first thing that we do is call the Given function. This sets up a Context that takes a function to arrange the test. Next, we call the With method on our Context to perform the operation that generates the result we want to test. Then we check as many conditions as we need by using the Observe function on the Outcome we created. Each outcome adds an action to the mutable Observations list.

When the xUnit framework encounters a BDDFactAttribute on a method, it first executes the method to populate the list of observations. For each observation, we return a BDDCommand. The xUnit framework then executes each command as a separate test case. This causes the command’s action to be executed and any failed assertions to throw errors which the xUnit framework displays in its test output.

Here’s the result. As you can see, the test name is extremely readable:

BDDTestResults

The code is definitely a little rough around the edges (*cough* mutable static variable *cough*). As always, I was impressed by how few lines of F# code it took to do what I wanted, but I was also impressed by how easy it was to extend xUnit.

F# Performance Testing: F# Types vs Structs and Classes

F# introduces Record Types and Descriminated Unions to the .NET universe. Although I knew that these types eventually boiled down to reference types, I wanted to make sure that nothing funny was going on behind the scenes when they got instantiated. The results weren’t terribly exciting, but I learned some important things about performance testing along the way.

 

First Attempt

The first thing I did was create a few basic types:

[<Struct>]
type StructWithImplicitConstructor =
   
val x : int
   
val y : int
   
[<Struct>]
type StructWithExplicitConstructor =
   
val x : int
   
val y : int
   
new (one, two) = {x = one; y = two}

[<Struct>]
type StructWithObjectConstructor(x:int, y:int) =
   
member this.X = x
   
member this.Y = y
   
type ObjectWithImplicitConstructor =
   
val x : int
   
val y : int
   
type ObjectWithExplicitConstructor =
   
val x : int
   
val y : int
   
new (one, two) = {x = one; y = two}
   
type ObjectWithObjectConstructor(x:int, y:int) =
   
member this.X = x
   
member this.Y = y

type RecordType =
    { x:int;
      y:int }
     
type UnionType =
    | X
of int
    | Y
of int

Then, I broke out a System.Diagnostics.Stopwatch and started testing how long it took to new up these objects. My first results varied wildly. JITer optimizations and garbage collection behaviors yielded bizarre and inconsistent results. After talking this over with coworkers Bill Wagner and Jay Wren, I made a second pass.

 

Getting Smarter

This time, I started with a more robust performance testing module:

module PerformanceTesting =
    let Time func =
        let stopwatch = new Stopwatch()
        stopwatch.Start()
        func()
        stopwatch.Stop()
        stopwatch.Elapsed.TotalMilliseconds
        
    let GetAverageTime timesToRun func = 
        Seq.init_infinite (fun _ -> (Time func))
        |> Seq.take timesToRun
        |> Seq.average
        
    let TimeOperation timesToRun =
        GC.Collect()
        GetAverageTime timesToRun
        
    let TimeOperations funcsWithName =
        let randomizer = new Random(int DateTime.Now.Ticks)
        funcsWithName
        |> Seq.sort_by (fun _ -> randomizer.Next())
        |> Seq.map (fun (name, func) -> name, (TimeOperation 100000 func))
    
    let TimeOperationsAFewTimes funcsWithName =
        Seq.init_infinite (fun _ -> (TimeOperations funcsWithName))
        |> Seq.take 50
        |> Seq.concat
        |> Seq.group_by fst
        |> Seq.map (fun (name, individualResults) -> name, (individualResults |> Seq.map snd |> Seq.average))

The above code makes it easy to time operations with greater accuracy. The TimeOperationsAFewTimes function takes a sequence of tuples containing the names of operations to test and the operations themselves. It runs the operations a few times and returns a sequence of tuples that contains the operation names and the average run time for each operation.

There are two important lessons for performance testing to take away from the example. First, the garbage collector is run between tests to make sure that each operation has a clean memory slate. Second, the order that the operations run in is varied between runs in order to help negate the effect of JITer optimizations in our tests.

(As an aside, the TimeOperationsAFewTimes function was really fun code to write. It was one of those moments that made me stop and take notice at the power of the F# language. I realize that the code might be a little difficult to read at first glance, though.)

 

Results

With the above functions, performance testing became a breeze:

printfn "Starting Tests..."
printfn ""

let tmpRec = {x = 0; y = 50}
 
PerformanceTesting.TimeOperationsAFewTimes 
    [ "RecordType", (fun () -> {x = 1; y = 50}|> ignore)
      "StructWithImplicitConstructor", (fun () -> new StructWithImplicitConstructor() |> ignore) 
      "ObjectWithObjectConstructor", (fun () -> new ObjectWithObjectConstructor(1, 50) |> ignore)
      "ObjectWithExplicitConstructor", (fun () -> new ObjectWithExplicitConstructor(1, 50) |> ignore)
      "StructWithObjectConstructor", (fun () -> new StructWithObjectConstructor(1, 50) |> ignore)
      "StructWithExplicitConstructor", (fun () -> new StructWithExplicitConstructor(1, 50) |> ignore)
      "CreatingUsingWith", (fun () -> { tmpRec with x = 1 } |> ignore)
      "UnionType", (fun () -> X 14 |> ignore)]
|> Seq.sort_by (fun (name, time) -> time)
|> Seq.iter (fun (name, time) -> printfn "%s: %f" name time)

printfn ""
printfn "Press Any Key To Continue..."
      
let tmp = System.Console.ReadKey()

The above code passes a list of tupled names and lamdas to the performance testing functions, sorts them from least to most expensive and prints the results.

 

Here are the results of a few runs:

Starting Tests...

CreatingUsingWith: 0.001973
StructWithExplicitConstructor: 0.001988
StructWithImplicitConstructor: 0.001992
UnionType: 0.002030
RecordType: 0.002064
ObjectWithExplicitConstructor: 0.002066
ObjectWithObjectConstructor: 0.002081
StructWithObjectConstructor: 0.002092

Press Any Key To Continue...

Starting Tests...

StructWithImplicitConstructor: 0.001407
CreatingUsingWith: 0.001408
RecordType: 0.001423
StructWithExplicitConstructor: 0.001436
ObjectWithObjectConstructor: 0.001438
UnionType: 0.001438
StructWithObjectConstructor: 0.001449
ObjectWithExplicitConstructor: 0.001455

Press Any Key To Continue...

Starting Tests...

RecordType: 0.001365
UnionType: 0.001374
StructWithImplicitConstructor: 0.001380
CreatingUsingWith: 0.001381
StructWithObjectConstructor: 0.001385
StructWithExplicitConstructor: 0.001386
ObjectWithExplicitConstructor: 0.001397
ObjectWithObjectConstructor: 0.001404

Press Any Key To Continue...

 

The Moral of The Story

The bottom line was that there were not any meaningful differences in the amount of time required to instantiate an object using any of the methods that I tried.

At first, my C and C++ roots were dumbfounded that value types didn’t beat the pants off of everything else. After Thinking critically, I realized that this makes a lot of sense in the managed world because the cost of creating reference types doesn’t include a hefty price for heap allocation.

The other thing that I proved to myself was that Record Types and Discriminated Unions are just as inexpensive to create as any other native .NET type.

Posted 10 April 2009 03:00 PM by cmarinos | no comments
Filed under: