F# and Unit Testing - Some New Developments

This past week, I've been focusing a lot of my attention on F# in terms of my presentations that I have been giving.  I'm busy preparing for the Philly ALT.NET meeting tomorrow night on the very subject.  An important aspect of some of the presentation has been unit testing.  There is some good news and some not so good news when it comes to this.  For those that have been following my pursuit of good unit tests in F# have known that xUnit.net has been a good option for being able to create static unit tests inside my classes instead of the pomp and circumstance of creating a new class and having member functions.

MbUnit Support for F#

Very recently Jeff Brown announced on his blog that he's now supporting tests without the requirement for the TestFixtureAttribute to be marked on your class in MbUnit.  This is quite helpful for F# tests and has joined the ranks of xUnit.net in terms of giving me another tool in my toolbelt.  There were other bugs that were filed that also were hindering good unit testing in F# that have been worked out as well. 

So, I should be able to do this below and everything should just work:

#light

#R @"D:\Program Files\Gallio\bin\MbUnit2\MbUnit.Framework.dll"
open MbUnit.Framework

let FilterCall protocol port =
  match(protocol, port) with
  | "tcp", _ when port = 21 || port = 23 || port = 25 -> true
  | "http", _ when port = 80 || port = 8080 -> true
  | "https", 443 -> true
  | _ -> false
 
[<Test>]
let FilterCall_WithHttpAndPort80_ShouldReturnTrue() =
  Assert.IsTrue(FilterCall "http" 80)

But...  this, is not the case.  It doesn't recognize that my tests exist.  Why? 

The New F# Release

With the newest release of F#, version 1.9.4.15, there was a change made that took the classes that encapsulated the tests and made it a static class.  So, if I were to look through .NET Reflector, it would look like this:

[CompilationMapping(SourceLevelConstruct.Module)]
public static class MbUnitTesting
{
    // Methods
    public static bool FilterCall(string protocol, int port) /// Method under test

    [Test]
    public static void FilterCall_WithHttpAndPort80_ShouldReturnTrue()
    {
        Assert.IsTrue(FilterCall("http", 80));
    }
}

This can be a problem, due to the fact that through reflection, any static class is marked abstract due to the fact you cannot create an instance of these classes.  This is a problem for the unit testing frameworks which cannot process abstract classes, yet.  So, this is a work in progress, but there has to be some strategy to get around this, as we have no way in reflection to determine if it is a static class easily.

The Workaround

The workaround for the issue is pretty simple, which is to actually use classes when creating your unit tests in F#.  I know it's a little bit of a pain, but the unit testing teams are aware of the issue and hopefully we'll have a fix soon enough.  But, in the mean time, we'll have to create the classes such as this in MbUnit:

[<TestFixture>]
type MbUnitTests = class
  new() = {}
 
  [<Test>]
  member x.FilterCall_WithHttpAndPort80_ShouldReturnTrue() =
    Assert.IsTrue(FilterCall "http" 80)
 
end

or in xUnit.net

type XUnitTests = class
  new() = {}
 
  [<Fact>]
  member x.FilterCall_WithHttpAndPort80_ShouldReturnTrue() =
    Assert.True(FilterCall "http" 80)
 
end


Then the Gallio Icarus Runner is free to pick up the results and runs as expected.  Like I said, hopefully the issue will be fixed soon.

kick it on DotNetKicks.com

2 Comments

Comments have been disabled for this content.