Tuesday, January 27, 2009

Minimal Unit Tests in F#

It's easy to get caught up in always building bigger, cooler more complex thingmajigs. Sometimes we forget our roots, take Unit Testing for exmaple, there's numerous frameworks and doodahs to facilitate that, but how slim and still provide value could it be. Over a cup of hot cacao I decided to find out. Here's the result:
#light

namespace Pencil.Unit

open System
open System.Diagnostics
open System.Collections.Generic

type IMatcher =
    abstract Match<'a> : 'a -> 'a -> bool
    abstract Format<'a> : 'a -> 'a -> string

module Unit =
    let (|>) x f = let r = f x in r |> ignore; r //work-around for broken debug info.
    let mutable Count = 0
    let Errors = List<String>()
    let Should (matcher:IMatcher) = fun e a ->
        Count <- Count + 1
        if matcher.Match e a then
            Console.Write('.')
        else
            let frame = StackTrace(true).GetFrame(1)
            let trace = String.Format(" ({0}({1}))",
                frame.GetFileName(),
                frame.GetFileLineNumber())
            Console.Write('F')
            Errors.Add((matcher.Format e a) + trace)

    let Equal = {
        new IMatcher with
            member x.Match e a = a.Equals(e)
            member x.Format e a =
                String.Format("Expected:{0}, Actual:{1}", e, a)}
open Unit
//Tests goes here
2 * 4 |> Should Equal 8
2 + 1 |> Should Equal 2

//Report the result
Console.WriteLine("{2}{2}{0} tests run, {1} failed.", Count, Errors.Count, Environment.NewLine)
if Errors.Count <> 0 then
    Errors |> Seq.iter
        (fun e -> Console.WriteLine("{0}", e))
And the output from the above:
.F

2 tests run, 1 failed.
(Expected:2, Actual:3 (F:\Pencil.Unit.fs(34))
Less than 40 lines of F# and we're on our way to unit testing goodness.

No comments:

Post a Comment