F# and Unit Testing - Some New Developments
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.