RSS
Facebook
Twitter

Thursday, January 23, 2014

One assert to rule them all

What’s wrong with Assert.X

Quick – what is the different between the following two lines:
Assert.IsTrue(result == expected);
Assert.AreEqual(expected, result);

Essentially both Asserts check for the same condition, in practice once fail we get very different error messages:

imageimage

While this example is easy to understand – when facing less than trivial test it can be downright impossible to understand the reason for the failure without a good error message (e.c. the 2nd message).

And that’s why unit testing courses have a slide or two about using the correct assert - because  using the more “general” IsTrue would give you a general error message which most of the time means that you power up your debugger just to find out what went wrong.
I cannot even start the count the times I caught this issue during code reviews or mentoring sessions. It seems such a time consuming activity – explaining yet again why a specific assert should be used instead of another.
Some developers don’t even know that many unit testing frameworks have specific assertion classes for string and collection and instead created complicated tests using the wrong assertion methods.

did you ever wrote the values in the assertion in the wrong order? – I know I did
For some reason someone decide a million years ago that the right order should be Assert.xyz(expected, result) – that is unless it’s InstanceOfType and you’re using MSTest.
It’s so unintuitive to think that the expectation comes before the actual value I’m checking that most developers gets it wrong the first time they write a test, gets a weird error message and understand the error of their ways while fixing a few tests.

The other day I’ve encountered another problem with the existing assert methods:
I did a small refactoring that caused a few of my tests to fail. The refactoring consisted of changing an Enumeration value to a class. Why on earth didn’t I get a compilation error – I got a runtime error instead due to the way Assert.AreEqual was defined.

Existing solutions

There are several approaches that have been tried before when dealing with asserts.
One approach is to use one of the fluent API libraries built on top of the existing unit testing framework API – in .NET there are many: NFluent, Should and good old Fluent Assertions to name a few.

A long time ago I read a blog post that suggested another solution:
I found this post after trying to understand unit tests that a co-worker wrote for his Silverlight code – he had a new assert type I never saw before Test.Assert which he used all over the place. At first I assumed that he made a mistake similar to using Assert.IsTrue but I quickly learnt that this one “assert” had magic inside that run the right check and output proper messages. After some digging I found a post by Jaffar Husain – the creator of this wonderful library explaining what he did.
I liked the way that his code harnessed to compiler and .NET to provide the right assertion error message without requiring any additional work from the user.
Unfortunately the code is no longer available, although you can probably find it somewhere on the internet – just look for “Silverlight unit testing framework”.

I know I wanted to create something similar of my own– for three main reasons:
  1. I couldn’t use the original code
  2. I wanted to have a different behavior for several asserts
  3. I was looking for a chance to use Expressions
And so I’ve created a new project on GitHub – AssertHelper

How to use AssertHelper

In order to use it all you have to do is specify your condition as a lambda using Expect.That method:
[Test]
public void CheckCompare()
{
    var myClass = new MyClass();

    var expected = 10;
    Expect.That(() => myClass.ReturnFive() == expected);
}

[Test]
public void CheckTrue()
{
    var myClass = new MyClass();

    
    Expect.That(() => myClass.ReturnFalse() == true);
}

[Test]
public void StringStartsWith() {
    var s1 = "1234567890";

    Expect.That(() => s1.StartsWith("456"));
}

[Test]
public void CollectionContains()
{
    var c1 = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

    Expect.That(() => c1.Contains(41));
}

I've even thrown in multiple assert support. Just put "&&" between your expectations/assertions to make sure all of them run even if some of them fail.

Getting AssertHelper

It’s out there either from GitHub or better yet use nuget and add a reference to your project.

It’s as simple as that…

Are there any limitations?

Yes there are, at the moment you can only use AssertHelper with NUnit.

I’m sure I didn’t cover all of the possible assertions out there, instead I choose the more commonly used:

ConditionNUnit

value == true

Assert.IsTrue

value == false

Assert.IsFalse

value == null

Assert.IsNull

value != null

Assert.IsNotNull

result == expected

Assert.AreEqual

result != expected

Assert.AreNotEqual

result is expectedType

Assert.IsInstanceOfType

aString.StartsWith(subString)

StringAssert.StartsWith

aString.EndsWith(subString)

StringAssert.EndsWith

aString.Contains(subString)

StringAssert.Contains

aCollection == expected

CollectionAssert.AreEqual

aCollection != expected

CollectionAssert.AreNotEqual

aCollection.Contains(value)

CollectionAssert.Contains

I do plan to add more support at the future, I’m still prioritizing future features - so let me know if some capability is really important for you.

I hope other developers would find this utility useful feel free to let me know what you think in the comments.

4 comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...