Unit Testing in C#
  • Unit testing in C#
  • Unit testing
    • What to test
    • When to test
    • Qualities of a good unit test suite
    • Qualities of a good unit test
    • Dealing with dependencies
    • Running the tests
  • NUnit
    • Quick glance at NUnit
    • Creating a NUnit test project
    • Anatomy of a test fixture
    • Lifecycle of a test fixture
    • Assertions
    • Asynchronous executions
    • Parameterized tests
    • Assumptions
    • Describing your tests
  • Moq
    • Quick glance at Moq
    • Method arguments
    • Method calls
    • Properties
    • Results
    • Callbacks
    • Exceptions
    • Events
    • Verifications
    • Base class
    • Mock customization
    • Implicit mocks
    • Mock repository
    • Custom matchers
    • Multiple interfaces
    • Protected members
    • Generic methods
    • Delegates
  • AutoFixture
    • Quick glance at AutoFixture
    • Fixture
    • Create and Build
    • Type customization
    • Data annotations
    • Default configurations
    • Building custom types
    • Relays
    • Tricks
    • Idioms
    • Integration with NUnit
    • Integration with Moq
    • Combining AutoFixture with NUnit and Moq
    • Extending AutoFixture
  • Advanced topics
    • Testing HttpClient
Powered by GitBook
On this page
  • Guard a method against null values
  • Constructor initialization
  • Value equality
  • Equality comparers
  1. AutoFixture

Idioms

PreviousTricksNextIntegration with NUnit

Last updated 4 years ago

Over time developers have agreed on certain best practices. Writing tests to ensure those best practices are followed through can be tedious.

Based on AutoFixture, the classes contained in the package help creating unit tests that quickly check if these best practices are properly followed.

The AutoFixture.Idioms package contains several assertions. Each assertion encapsulates a unit test that tests a specific expectation on the system under test.

All assertions implement the interface IIdiomaticAssertion. This interface exposes a plethora of overloads of the Verify methods accepting everything from a set of assemblies down to a single member.

The abstract class IdiomaticAssertion offers a basic implementation of all overloads but the ones at the end of the tree (constructors, methods, properties, fields).

Given how the IdiomaticAssertion class works, developers can target a specific member or the whole type. It will be up to the author of the assertion to make sure that only the suitable members are tested.

var fixture = new Fixture();
var assertion = fixture.Create<MyFakeAssertion>();
assertion.Verify(typeof(TestClass));
assertion.Verify(typeof(TestClass).GetMethods());
assertion.Verify(typeof(TestClass).GetMethod(nameof(TestClass.TestMethod)));

Here is a list of scenarios that can be accellerated by using assertions available in the AutoFixture.Idioms package.

Guard a method against null values

When writing methods or constructors, it is good practice to protect them against unsupported null values. The GuardClauseAssertion verifies that the parameters passed to a method or a constructor are properly checked against null values.

Assuming a test class like the following one (for semplicity we will use string as dependencies):

public class TestClass
{
    private readonly string _firstDependency;
    private readonly string _secondDependency;

    public TestClass(string firstDependency, string secondDependency) 
    {
        _firstDependency = firstDependency ?? throw new ArgumentNullException(nameof(firstDependency));
        _secondDependency = secondDependency ?? throw new ArgumentNullException(nameof(secondDependency));
    }

    public void DoSomething(string parameter)
    {
        if (parameter == null) throw new ArgumentNullException(nameof(parameter));

        ...
    } 
}

The snippet below will test that every parameter of the constructor is guarded against null values.

[Test]
public void Constructor_is_guarded_against_nulls()
{
    // ARRANGE
    var fixture = new Fixture();
    var assertion = fixture.Create<GuardClauseAssertion>();

    // ACT & ASSERT
    assertion.Verify(typeof(TestClass).GetConstructors());
}

If any of the nullable parameters of the constructor were not to be guarded, an exception would be thrown and the unit test would fail.

To be noted that without the help of the Idioms package, the developer would expected to write N+1 unit tests, only for the constructor: one for each incoming parameter and one for the correct initialization of the system under test.

[Test]
public void FirstDependency_is_required()
{
    // ARRANGE
    var fixture = new Fixture();

    // ACT & ASSERT
    Assert.That(() => new TestClass(null, fixture.Create<string>()), Throws.ArgumentNullException);
}

[Test]
public void SecondDependency_is_required()
{
    // ARRANGE
    var fixture = new Fixture();

    // ACT & ASSERT
    Assert.That(() => new TestClass(fixture.Create<string>(), null), Throws.ArgumentNullException);
}

[Test]
public void TestClass_can_be_instantiated()
{
    // ARRANGE
    var fixture = new Fixture();

    // ACT & ASSERT
    Assert.That(() => new TestClass(fixture.Create<string>(), fixture.Create<string>()), Throws.Nothing);
}

Besides the annoyance of creating multiple repetitive tests, the tests above will need to be fixed every time a new change in the constructor were to occur.

The same assertion can be used to verify the correct implementation of normal methods.

[Test]
public void DoSomething_is_guarded_against_nulls()
{
    // ARRANGE
    var fixture = new Fixture();
    var assertion = fixture.Create<GuardClauseAssertion>();

    // ACT & ASSERT
    assertion.Verify(typeof(TestClass).GetMethod(nameof(TestClass.DoSomething)));
}

Constructor initialization

Another good practice is to initialize read-only properties with values passed to the constructor. The ConstructorInitializedMemberAssertion can be used to verify that these properties have been initialized with the value passed to the constructor via same-name arguments.

Let´s take this class as test subject.

public class TestClass
{
    public TestClass(string parameter, int value)
    {
        Parameter = parameter ?? throw new ArgumentNullException(nameof(parameter));
        Value = value;
    }

    public string Parameter { get; }

    public int Value { get; }
}

The ConstructorInitializedMemberAssertion can be used to verify that the properties Parameter and Value contain the same values passed to the constructor.

[Test]
public void Properties_are_initialized_by_constructor()
{
    // ARRANGE
    var fixture = new Fixture();
    var assertion = fixture.Create<ConstructorInitializedMemberAssertion>();

    // ACT & ASSERT
    assertion.Verify(typeof(TestClass));
}

Commenting any of the two assignments in the TestClass constructor will cause the test in the snippet above to fail.

Value equality

When working with value objects (i.e. objects who are distinguishable by the state of their properties and not by their identity), handling correctly the equality checks is paramount.

In C#, equality checks are customized by overriding the virtual methods Object.Equals and Object.GetHashCode.

When overriding thesem methods, developers must make sure that the properties of equality are satisfied. These are:

  • Reflexive: an object must be equal to itself. a == a

  • Symmetric: if an object is equal to another, the second is also equal to the first. (a == b) => (b == a)

  • Transitive: if an object is equal to another, and this is equal to a third, the first is equal to the third. (a == b, b == c) => (a == c)

On top of these logic requirements, there are additional requirements:

  • A reference type cannot be equal to null,

  • A class overriding Object.Equals must override Object.GetHashCode too,

  • Applying repetedly Object.Equals to the same object must return the same result,

  • Applying repetedly Object.GetHashCode to the same object must return the same result,

  • Testing for equality with new object() must return false.

AutoFixture.Idioms contains assertions that can reduce the amount of code needed for the tests.

Specifically, these assertions are available:

  • EqualsNewObjectAssertion verifies that Object.Equals is implemented so that x.Equals(new object()) is always false,

  • EqualsNullAssertion verifies that Object.Equals is implemented so that x.Equals(null)) is always false,

  • EqualsSelfAssertion verifies that Object.Equals is implemented so that x.Equals(x)) is always true,

  • EqualsSuccessiveAssertion verifies that Object.Equals is implemented so that calling x.Equals(y) several times returns always the same value,

  • GetHashCodeSuccessiveAssertion verifies that Object.GetHashCode is implemented so that calling x.GetHashCode() several times returns always the same value.

Since most likely all the equality assertions needs to be checked, these can be combined into a single one specializing the CompositeIdiomaticAssertion abstract class.

Let's consider this class as example

public class SampleValueObject
{
    public string StringValue { get; set; }

    public int IntValue { get; set; }

    public override bool Equals(object obj)
    {
        if (obj is SampleValueObject other)
        {
            return string.Equals(StringValue, other.StringValue, StringComparison.Ordinal) && Equals(IntValue, other.IntValue);
        }

        return false;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(typeof(SampleValueObject), StringValue);
    }
}

We can define our own EqualityAssertion as follows

public class EqualityAssertion : CompositeIdiomaticAssertion
{
    public EqualityAssertion(ISpecimenBuilder builder) : base(CreateChildrenAssertions(builder)) { }

    private static IEnumerable<IIdiomaticAssertion> CreateChildrenAssertions(ISpecimenBuilder builder)
    {
        yield return new EqualsNewObjectAssertion(builder);

        yield return new EqualsNewObjectAssertion(builder);

        yield return new EqualsSelfAssertion(builder);

        yield return new EqualsSuccessiveAssertion(builder);

        yield return new GetHashCodeSuccessiveAssertion(builder);
    }
}

We can then create a simple unit test like the one below

[Test]
public void Equality_is_correctly_implemented()
{
    // ARRANGE
    var fixture = new Fixture();
    var assertion = fixture.Create<EqualityAssertion>();

    // ACT & ASSERT
    assertion.Verify(typeof(SampleValueObject));
}

The main advantage of this approach is that developers can later on expand the EqualityAssertion class by addition additional child assertions.

Equality comparers

When it's not possible to customize the behavior of the class, or simply it's preferrable to have multiple equality comparison strategies (like the case of the different string comparers), developers can create their own comparers by implementing the interface IEqualityComparer<T>.

This interface exposes two methods, bool Equals(T,T) and int GetHashCode(T), and the same rules applying for value equality need to be followed when implementing this interface.

Starting from version 4.14.0, AutoFixture includes a EqualityComparerAssertion that can be used to validate the implementation of the custom equality comparer.

Let's consider the following equality comparer for the class SampleValueObject displayed above

public class SampleValueObjectEqualityComparer : IEqualityComparer<SampleValueObject>
{
    public bool Equals(SampleValueObject x, SampleValueObject y)
    {
        if (x is null && y is null) return true;

        if (x is null ^ y is null) return false;

        return string.Equals(x.StringValue, y.StringValue) && Equals(x.IntValue, y.IntValue);
    }

    public int GetHashCode(SampleValueObject obj)
    {
        _ = obj ?? throw new ArgumentNullException(nameof(obj));

        return HashCode.Combine(typeof(SampleValueObject), obj.StringValue, obj.IntValue);
    }
}

By using the EqualityComparerAssertion, we can test all the equality properties listed above in a single unit test.

[Test]
public void Equality_comparer_is_correctly_implemented()
{
    //ARRANGE
    var fixture = new Fixture();
    var assertion = fixture.Create<EqualityComparerAssertion>();

    // ACT & ASSERT
    assertion.Verify(typeof(SampleValueObjectEqualityComparer));
}

Unfortunately, as of today, C# doesn't have a good built-in support for value objects (). This means that a lot of work has to be put to correctly handle value equality.

As of today, developers are left to write unit tests that prove that the symmetric and transitive properties are respected but they can be found in . Since this package is based on AutoFixture 3.36, it might not work with newer versions but developers can take inspiration from .

AutoFixture.Idioms
see Records coming in C# 9.0
this package
its source code