Fun with C#: Create fluent assertions for your domain

When writing tests, we should strive to make the test code just as good (if not better) as the production code. Embedding your domain’s language into your test assertions can help a lot with that.

Take the following example:

[TestMethod]
public void MeatLover()
{
    var pizzaMaker = new PizzaMaker();

    var meatLoverPizza = pizzaMaker.MakeMeatLover();

    Assert.IsTrue(meatLoverPizza.Toppings.Any(topping => topping.Name == "Pepperoni"));
    Assert.IsTrue(meatLoverPizza.Toppings.Any(topping => topping.Name == "Italian sausage"));
    Assert.IsTrue(meatLoverPizza.Toppings.Any(topping => topping.Name == "bacon"));
    Assert.IsTrue(meatLoverPizza.Toppings.Any(topping => topping.Name == "ham"));
    Assert.IsTrue(meatLoverPizza.Toppings.Any(topping => topping.Name == "mozzarella"));
}

Our PizzaMaker class is supposed to make meat lover pizzas, so whenever it does, it’s expected to have a specific list of toppings on it. The test above captures that requirement. However, the assertions are very noisy. I’d like to have my test read like so instead:

[TestMethod]
public void MeatLover()
{
    var pizzaMaker = new PizzaMaker();

    var meatLoverPizza = pizzaMaker.MakeMeatLover();

    meatLoverPizza.ShouldHaveToppings("Pepperoni", "Italian sausage", "bacon", "ham", "mozzarella");
}

Notice that code in the test no longer has things like calls to Assert, or Any, or lambdas. It only has the bare minimum required by the C# compiler, and it is a lot easier to read so we understand the actual requirement being verified. Writing the tests that way allows me to collaborate with other developers, QA, and Business Analysts, as they can follow the code a lot more easily.

So, where does the ShouldHaveToppings method for the Pizza class comes from? A simple Extension Method:

public static class PizzaAssertionExtensions 
{
    public static void ShouldHaveToppings(this Pizza pizza, params string[] toppingNames)
    {
        foreach (var toppingName in toppingNames)
        {
            Assert.IsTrue(pizza.Toppings.Any(topping => topping.Name == toppingName),
                "Topping missing: " + toppingName);
        }
    }
}

To make things even better, I’d like my code to read like this:

meatLoverPizza.Should().HaveToppings("Pepperoni", "Italian sausage", "bacon", "ham", "mozzarella");

That way, my extension method would simply be called HaveToppings. In order to implement it that way, I’d look into making my fluent assertions extend FluentAssertions.

In summary, instead of just using plain calls to Assert, create fluent assertions that map to the nouns and verbs that exist in your domain.

Advertisements

  1. #1 by Mr Fluent Assertions (@ddoomen) on February 23, 2017 - 2:12 am

    I blogged extensively on how to use the internal API to build your own extensions to FluentAssertions. http://www.continuousimprover.com/2016/05/the-definitive-guide-to-extending.html

    • #2 by claudiolassala on February 25, 2017 - 8:05 pm

      That’s excellent. Thanks for letting me know. I’ll definitely check it out! 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: