Chris Surfleet's MentalReboot

Code insights tinged with a spot of insanity

This is the second of my posts on the brilliant FluentAssertions library. You can find the rest of the posts here:

OK, so today we’ll jump right into the various assertions we can make on dates, times and collections.

Dates and Times

This is where the wonder of FluentAssertions really starts to shine through, DateTime assertions become hugely more readable. Take the simplest example:

Assert.AreEqual(DateTime.Parse("08/10/2008"), myDateValue);

Urgh. This does not read at all well, to start with I’m in the UK but I’m sure a lot of you guys over the pond read that as 10th August. (I know, it could have been written a little better, I’m labouring the point OK?) Let’s try it again:

myDateValue.Should().Be(8.October(2008));

Great, it reads better, and there is no chance of confusion over our intention. How about this:

myDateValue.Should().BeWithin(5.Days()).After(10.January(2010));
I really don’t need to explain what we are asking above which is absolutely brilliant. You can call your product owner over to query how some feature should work, and they can see that you are asking the correct questions – without having to know how to write a single line of c# code.

You can chain your expectations using the And keyword just as you could for strings as well:

myDateValue.Should().BeWithin(5.Days()).After(10.January(2010))
  .And.BeAtLeast(6.Hours()).Before(15.January(2010).At(21, 00));

It’s worth playing around with code completion, but some of the DateTime methods available include: BeBefore, BeAfter, BeWithin, HaveDay/Month/Year/Hour/Minute/Second, BeAtLeast, BeMoreThan.

Collections

Next up there are a few really nice collection assertions. Let’s say you want to ensure that a collection is empty. In MsTest you would write:

Assert.AreEqual(0, items.Count);

You could obviously improve this to:

items.Count.Should().Be(0);

But you can actually write this instead:

items.Should().BeEmpty();

Once again, the language of your assertions is very similar to the natural language used to define acceptance criteria.

Collections is where I find myself chaining assertions the most. You could write something similar to:

items.Should().HaveCount(5)
  .And.NotContainNulls()
  .And.OnlyHaveUniqueItems()
  .And.NotContain(15);

There are a multitude of assertions available for inspecting the contents of your collection. This example wouldn’t work but will show you some the various methods available to you:

items.Should().Contain(new[] {1, 2, 3})
  .And.ContainInOrder(new[] {7, 10})
  .And.HaveSameCount(someOtherCollection)
  .And.NotBeSubsetOf(someOtherCollection)
  .And.ContainItemsAssignableTo<int>();

You can even use predicates:

items.Should().OnlyContain(n => n > 3)
  .And.Contain(n => n > 40);

So the collection should only contain numbers larger than three, and at least one item larger than 40. This isn’t as readable however so I generally tend to create my own custom assertions in these cases, which I’ll go through in part 4 of this series.

So, that’s all you need to know about basic FluentAssertions. I’ll admit to finding this series tricky to write so far as all the code examples are so obvious, but next time we’ll dig into some of the more in depth stuff you can do with exception and event assertions. I’ll finish up in part 4 by describing how you can create your own assertions library to keep your tests clean and readable. Thanks for reading!

Did you find this post useful or interesting? I'd be really grateful if you could return the favour by clicking this google link:



I recently discovered a very cool project on NuGet – FluentAssertions. This is a really funky little library that vastly improves assertions in your unit tests, whether you are using NUnit or MsTest. I’ve been using it for a few weeks now and thought I would share some of the cool things you can do with this framework. I’ll start with the simple stuff and over the next few weeks show some of the assertions you can make on dates and collections, then exceptions and events and creating your own assertions and using this to map unit tests more closely to acceptance criteria. Some of this will duplicate the documentation on codeplex, but I’ll go into more detail about how to use the techniques in practice.

Firstly, go and NuGet FluentAssertions into one of your test projects. Next go and find some string assertion, which will probably look something like this (I’m using MsTest):

Assert.AreEqual("123,456,789", result);

You can replace this with the following FluentAssertion:

result.Should().Be("123,456,789");

OK, so this already reads much more nicely, which is great, but the reporting of a failed assertion is also improved. If result was the wrong value with the AreEqual assertion you would get the following:

Assert.AreEqual failed. Expected:<123,456,789>. Actual:<123,458,789>.

This is alright, but it could be tricky to notice the 8 which should be a 6. Compare that to the FluentAssertion’s reporting:

Expected string to be
"123,456,789", but
"123,458,789" differs near "8,7" (index 6).

 

Not only have the expected and actual values been lined up vertically to aid scanning, but the message even highlights the point in the string the values differ.

You’ll notice if you type Should() and look at the code completion list that you get a lot of options. You could use StartWith or EndWith, or even do a wildcard match using Match. You can also chain your expectations together using the And property, so instead of:

result.Should().NotBeNullOrEmpty();
result.Should().NotMatch("*,");
result.Should().NotMatch(",*");

You can write:

result.Should().NotBeNullOrEmpty()
  .And.NotMatch("*,")
  .And.NotMatch(",*");

 

This quickly makes your unit tests much more readable.

You can ignore casing easily, so instead of

Assert.AreEqual("hello", result.ToLower());

You can use

result.Should().BeEquivalentTo("hello");

This is just the options available for strings. There are all the options you would expect for numbers too:

result.Should().BeGreaterOrEqualTo(5);
result.Should().BeInRange(2, 27);
result.Should().BeNegative();

The cool thing about these expectations is that they will work for any object implementing the IComparable interface too!

Finally, each expectation can take a reason string too, which will be printed out along with an expectation error:

result.Should().Match("JM*", "because result should be a valid UK insurance number");
Expected string to match "JM*" because result should be a valid UK insurance number,
but "INVALID" does not match.

This is not only helpful when the test fails, but when looking at the test code it reads much more like a set of acceptance criteria, so the intent of your test is more obvious.

I hope this will encourage a few of you to use this great library – next time I’ll go through the things you can do with collections, dates and times, and then get into creating your own custom expectations.

Did you find this post useful or interesting? I'd be really grateful if you could return the favour by clicking this google link:



Month List

Sign in

Top Programming Sites Vote for me at Fuelmyblog