Unit tests in F# – friendly names

After few years of writing unit tests I have decided to replace MSTest framework with nUnit. There are few reasons of this. MSTest hasn’t change much since it was introduced in 2010 so it is far behind it’s competitors. It lacks of Assert.Throws. There is ExpectedException attribute but it isn’t consistent with other asserts and test coverage analyzers have problem with this. It doesn’t support input parameters. At the end it runs tests slowly. I haven’t noticed it before because I was using ReSharper. Without this tool running tests in UnitTest window takes years. Later I will mention about TestClass attribute.

Changing a framework isn’t all what I have done. I have also changed a language of my tests to F#. Why? Because I want to learn something new. Writing tests in other language seems to be easy and painless way to learn it. Tests can be more readable, as an addition.

Unit Test Session window from ReSharper looks like this when tests are written in C# and MSTest:

Unit tests in C#

Unit tests in C#

What do we have here? A long name where words are combined with underscore. It can be readable but is not natural. It is a little bit uncomfortable to read names like “is_time_23_59_59_properly_parsed”. When we try to decode this name we can write it like this “is time 23:59:59 properly parsed”? So why we can’t write test name like this? There are method name limitations in C# of course. But it is possible to do it in F#. The screen below shows the same unit tests written in F#. Words are separated by space, hour parts with colon. We can write test names using almost every sign we want to. Isn’t it nice?

Unit tests in F#

Unit tests in F#

Below is one of unit tests presented on the first screen.

namespace BerlinClock.UnitTests
{
    [TestClass]
    public class TimeUnitTests
    {
        //arrange
        var time = "23:59:59";

        //act
        var parsedTime = Time.Parse(time);

        //assert
        Assert.AreEqual(23, parsedTime.Hour);
        Assert.AreEqual(59, parsedTime.Minute);
        Assert.AreEqual(59, parsedTime.Second);
    }

First try of writing the same test in F# isn’t perfect. We still have a method name written with the same rules as in C#. We have no braces, there are indents only, like in Python or Ruby.

module TimeUnitTests

open Microsoft.VisualStudio.TestTools.UnitTesting;
open BerlinClock.Classes

[<TestClass>]
type TimeUnitTests() = 
    
    [<TestMethod>]
    member this.is_time_23_59_59_properly_parsed() = 
        //arrange
        let time = "23:59:59"
        
        //act
        let parsedTime = Time.Parse(time)

        //assert
        Assert.AreEqual(23, parsedTime.Hour)
        Assert.AreEqual(59, parsedTime.Minute)
        Assert.AreEqual(59, parsedTime.Second)

But we can try to surround a name with double grave accents and write inside everything we want to:

    [<TestMethod>]
    member this.``is time 23:59:59 properly parsed``() = 
        //arrange
        let time = "23:59:59"
        
        //act
        let parsedTime = Time.Parse(time)

        //assert
        Assert.AreEqual(23, parsedTime.Hour)
        Assert.AreEqual(59, parsedTime.Minute)
        Assert.AreEqual(59, parsedTime.Second)

Now it looks better. We have readable name and it will be shown in window with unit tests. This code is available on my GitHub.

F# allows us to assign functions to modules instead of classes. But MSTest doesn’t allow to skip TestClass attribute which should be placed above a class which holds unit tests. When we will remove this attribute then no test will be found with test runner. This is the next reason to change unit test’s framework. When migrating to nUnit we have to install it’s libraries and runner and attach new reference to our project. After that we can change TestClass attribute to TextFixture and TestMethod to Test (GitHub).


[<TestFixture>]
type TimeUnitTests() = 

    [<Test>]
    member this.``is time 23:59:59 properly parsed``() = 
    //arrange
    let time = "23:59:59"
 
    //act
    let parsedTime = Time.Parse(time)

    //assert
    Assert.That(parsedTime.Hour, Is.EqualTo(23))
    Assert.That(parsedTime.Minute, Is.EqualTo(59))
    Assert.That(parsedTime.Second, Is.EqualTo(59))

The next step is to move test methods outside a class and assign them to a module. We do this by using keyword let instead of member this. We have to remove unnecessary indents also. We can remove now class’ code with TextFixture attribute. Thanks to nUnit our tests are still available in unit test windows and for test runners (GitHub).


[<Test>]
let ``is time 23:59:59 properly parsed``() = 
    //arrange
    let time = "23:59:59"
 
    //act
    let parsedTime = Time.Parse(time)

    //assert
    Assert.That(parsedTime.Hour, Is.EqualTo(23))
    Assert.That(parsedTime.Minute, Is.EqualTo(59))
    Assert.That(parsedTime.Second, Is.EqualTo(59))

Our code looks nice right now. It is more readable. There will be some improvements in the future because of nUnit features. More about this in next post.

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*
Website