Since the release of C#8, like every release before hand, I have looked at the language features and thought “I like some, but some I do not”. In earlier versions the features have grown on me and I find using them pretty natural now but there are some features that still feel “odd”. C# 8 was no different and one of the features that felt “odd” was “Using Declarations” (link).

In a number of examples in which I’d seen it being used it didn’t seem to make things clearer but actually look like it hides what it is doing. This is a concern for me for readability as well as other developers picking up my code.

I didn’t see a use for it until now.

FluentAssertions

If you’re not using FluentAssertions why not? It’s a great library for improving your unit test assertions flow and with the fluent syntax allows readability to be clearer about the intent of the test. I would strongly recommend watching “Improving Unit Tests with Fluent Assertions” by Jason Roberts on Pluralsight.

FluentAssertions has the concept of “AssertionScopes” which harness the power of IDisposable so is used in a using block.

Let’s see how Using Declarations can tidy up the Assert sections of your unit tests.

Basic Test

Let’s start with a basic test.

[Fact]
public void Test1()
{
    var sut = new Money(123, "GBP");

    sut.Value.Should().Be(123);
    sut.Currency.Should().Be("GBP");
}

This is a very simple test and it checks the properties are set correctly from the constructor.

Breaking the test

If we now break the test on purpose we can get FluentAssertions to output an error message for us.

[Fact]
public void Test2()
{
    var sut = new Money(555, "USD");

    sut.Value.Should().Be(123);
    sut.Currency.Should().Be("GBP");
}

If the above is now run the following message is displayed in your test runner.

Expected sut.Value to be 123M, but found 555M.
   at FluentAssertions.Execution.XUnit2TestFramework.Throw(String message)
   at FluentAssertions.Execution.TestFrameworkProvider.Throw(String message)
   at FluentAssertions.Execution.DefaultAssertionStrategy.HandleFailure(String message)
   at FluentAssertions.Execution.AssertionScope.FailWith(Func`1 failReasonFunc)
   at FluentAssertions.Execution.AssertionScope.FailWith(Func`1 failReasonFunc)
   at FluentAssertions.Execution.AssertionScope.FailWith(String message, Object[] args)
   at FluentAssertions.Numeric.NumericAssertions`1.Be(T expected, String because, Object[] becauseArgs)
   at FluentAssertsionWithUsings.UnitTest1.Test2() in C:\Users\adam\source\repos\FluentAssertionWithUsings\FluentAssertionWithUsings\UnitTest1.cs:line 36

We can see the message is telling us what went wrong …

Expected sut.Value to be 123M, but found 555M.

… but only showed us the first error message.

Added An AssertionScope

We can resolve this by adding an AssertionScope (link) into the test. This allows for all the assertions to execute before the test completes, not just error on the first failed assertion.

[Fact]
public void Test3()
{
    var sut = new Money(555, "USD");

    using (new AssertionScope())
    {
        sut.Value.Should().Be(123);
        sut.Currency.Should().Be("GBP");
    }
}

We have now wrapped our asserts in a using block and created a new scope for the duration of the using block.

On running the unit test it will now fail with the following error message:

Expected sut.Value to be 123M, but found 555M.

Expected sut.Currency to be “GBP”, but “USD” differs near “USD” (index 0).

Now in this instance we can see all of the failed asserts however adding the asserts into a using block makes the test look more complicated than it is.

Let’s take a look at how we can clean this up.

Applying Using Declaration

If we refactor the test above and apply some C#8 syntatic sugar we can make it look a little cleaner.

[Fact]
public void Test4()
{
    var sut = new Money(555, "USD");

    using var scope = new AssertionScope();
    sut.Value.Should().Be(123);
    sut.Currency.Should().Be("GBP");
}

We’ve now removed the additional braces and the indentation and allowed for the assert lines to be as before. The C#8 using declaration instantiates the AssertionScope to be applied to the asserts below.

The issue for me is the control over the scoping of the using block when implenting this new syntax but in this intance I am happy with it. The using block will go out of scope at the end of the method and if you have all your asserts at the end of the test (as you should following the AAA unit test layout!) then they are the last items you have so it’s fine.

The test runner output for the above is the same as Test3 earlier.

Expected sut.Value to be 123M, but found 555M.

Expected sut.Currency to be “GBP”, but “USD” differs near “USD” (index 0).

The unit test is now back to being pretty clean again.

Conclusion

In this post I have shown how to use AssertionScopes and the potential benefit to your unit tests. Also, using the newer C#8 Using Declaration syntax, it allows the unit tests to use the scoping functionality without making the tests look more complicated than they are.

It’s safe to say embracing new language constructs can take time for any developer of any experience but it’s good to play and it’s good to come back to and then one day it may stick.

Any questions/comments then please contact me on Twitter @WestDiscGolf