
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:
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?
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.