Jolt.Testing : General-Purpose Assertions

Note: This page describes the core general-purpose assertions in the Jolt.Testing library. For a description of assertion adapters for a third party test framework, please visit this page.

When authoring unit tests, it is important to ensure that the tests are both readable and relatively brief. This ensures that the intent of the test is clear, and that it is easy to diagnose failures; ideally a failed assertion should clearly describe what is wrong in the program (though most times, we need to debug!). Unit test frameworks facilitate this notion by providing a set of assertions methods that encapsulate frequently repeated checks. The assertion methods are compact and clearly convey what the unit test is asserting. For instance, the Assert.Throws<T> NUnit assertion conveniently encapsulates the following construct.

[Test]
public void ThrowsException()
{
    // Compare with Assert.Throws<IOException>(() => m_myTestInstance.Operation());

    try
    {
        m_myTestInstance.Operation();
        Assert.Fail("IOException expected, none thrown.")
    }
    catch(IOException) { }
    catch(Exception ex)
    {
        Assert.Fail("IOException expected, {0} thrown.", ex.GetType())
    }
}

Jolt.NET assertions aim to augment the existing set of assertions by following the same design principles. Furthermore, the assertions are designed so that they may be easily integrated with any third-party unit test framework.

Table of Contents

  1. Assertion Failure Reporting
  2. Design Scope
  3. Usage Examples
    1. Asserting the implementation of an equality operator
    2. Asserting the implementation of an equality operator, for IEquatable
    3. Asserting the implementation of an equality operator, for IComparable
    4. Asserting the implementation of an equality operator, for IEqualityComparer

Assertion Failure Reporting

Many unit test frameworks utilize exceptions to report assertion failures. Since the Jolt.NET assertions are intended to be used in conjunction with other frameworks, introducing additional exceptions will likely degrade performance. Consequently, the result of an assertion will always be returned to the caller as part of the assertion function's return value, and is paired with a message describing the failure (if applicable). The AssertionResult class encapsulates this idiom, and is the base class for all assertion return values.

using Jolt.Testing.Assertions;
 
public class AssertionResult
{
    public bool Result { get; }
    public string Message { get; }
}

Design Scope

The Jolt.NET general purpose assertion library is designed to support the following features.
  • Facilitates integration with many test frameworks by providing framework-agnostic assertions and operations
  • Assertion Categories
    • Verification of requirements for implementations of equality operators, in their various forms
      • IEquatable<T>
      • IComparable<T>
      • IEqualityComparer<T>
      • Object.Equals(Object)

Usage Examples


The EqualityAxiomAssertion<T> class verifies that the given type T implements Object.Equals(Object) according to the prescribed equality axioms and hash-code requirements (see the Object.Equals() documentation for more information). Since many of the axioms require comparison of two or more distinct references, EqualityAxiomAssertion<T> requires that the user provide a factory to return such instances. The factory is represented by an implementation of the IArgumentFactory<T> interface.

The following example demonstrates how to use EqualityAxiomAssertion<T> to validate the equality semantics of a user-defined type.

using System.Diagnostics;
using Jolt.Testing.Assertions;

public partial class TypeToValidate
{
    public string Name { get; set; }
    public int Value { get; set; }

    public override bool Equals(object other)
    {
        TypeToValidate rhs = other as TypeTypeToValidate;
        return rhs != null && Name == rhs.Name && Value == rhs.Value;
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode() ^ Value.GetHashCode();
    }
}

public partial class TypeFactory : IArgumentFactory<TypeToValidate>
{
    public TypeToValidateCreate()
    {
        return new TypeToValidate() { Name = "instanceName", Value = 12345 };
    }

    public void Modify(ref TypeToValidate instance)
    {
        instance.Name = "newInstanceName";
        instance.Value += 200;
    }
}

void ValidateEqualitySemantics()
{
    // var is used for brevity
    var assertion = new EqualityAxiomAssertion<TypeToValidate>(new TypeFactory());
    AssertionResult isValid = assertion.Validate();

    Debug.Assert(isValid.Result, isValid.Message);
}


The EquatableAxiomAssertion<T> class extends EqualityAxiomAssertion<T> and validates only IEquatable<T> implementations. As a result, the user-supplied factory must now implement IEquatableFactory<T> as this new interface meets the constraint required by the assertion.

The following example demonstrates how to use EquatableAxiomAssertion<T> to validate the equality semantics of a user-defined type, building on the types provided in the EqualityAxiomAssertion<T> example.

using System;
using System.Diagnostics;
using Jolt.Testing.Assertions;

public partial class TypeToValidate : IEquatable<TypeToValidate>
{
    public bool Equals(TypeToValidate instance)
    {
        return instance != null && Name == instance.Name && Value == instance.Value;
    }
}

public partial class TypeFactory : IEquatableFactory<TypeToValidate>
{
    TypeToValidate IEquatableFactory<TypeToValidate>.Create()
    {
        return Create();
    }

    void IEquatableFactory<TypeToValidate>.Modify(ref TypeToValidate instance)
    {
        Modify(ref instance);
    }
}

void ValidateEqualitySemantics()
{
    // var is used for brevity
    var assertion = new EquatableAxiomAssertion<TypeToValidate>(new TypeFactory());
    AssertionResult isValid = assertion.Validate();

    Debug.Assert(isValid.Result, isValid.Message);
}


The ComparableAxiomAssertion<T> class extends EqualityAxiomAssertion<T> and validates only IComparable<T> implementations. As a result, the user-supplied factory must now implement IComparableFactory<T> as this new interface meets the constraint required by the assertion.

The following example demonstrates how to use ComparableAxiomAssertion<T> to validate the equality semantics of a user-defined type, building on the types provided in the EqualityAxiomAssertion<T> example.

using System;
using System.Diagnostics;
using Jolt.Testing.Assertions;

public partial class TypeToValidate : IComparable<TypeToValidate>
{
    public int CompareTo(TypeToValidate instance)
    {
        return Value.CompareTo(instance.Value);
    }
}

public partial class TypeFactory : IComparableFactory<TypeToValidate>
{
    TypeToValidate IComparableFactory<TypeToValidate>.Create()
    {
        return Create();
    }

    void IComparableFactory<TypeToValidate>.Modify(ref TypeToValidate instance)
    {
        Modify(ref instance);
    }
}

void ValidateEqualitySemantics()
{
    // var is used for brevity
    var assertion = new ComparableAxiomAssertion<TypeToValidate>(new TypeFactory());
    AssertionResult isValid = assertion.Validate();

    Debug.Assert(isValid.Result, isValid.Message);
}


The EqualityComparerAxiomAssertion<T> class extends EqualityAxiomAssertion<T> and validates an IEqualityComparer<T> implementation. As a result, the assertion requires an instance of the comparer being validated, in addition to the factory that constructs instances of T.

The following example demonstrates how to use EqualityComparerAxiomAssertion<T> to validate the equality semantics of a user-defined type, building on the types provided in the EqualityAxiomAssertion<T> example.

using System.Collections.Generic;
using System.Diagnostics;
using Jolt.Testing.Assertions;

public TypeComparer : IEqualityComparer<TypeToValidate>
{
    public bool Equals(TypeToValidate lhs, TypeToValidate rhs)
    {
        return lhs.Equals(rhs);
    }

    public int GetHashCode(TypeToValidate instance)
    {
        return instance.GetHashCode();
    }
}

void ValidateEqualitySemantics()
{
    // var is used for brevity
    var assertion = new EqualityComparerAxiomAssertion<TypeToValidate>(new TypeFactory(), new TypeComparer());
    AssertionResult isValid = assertion.Validate();

    Debug.Assert(isValid.Result, isValid.Message);
}

Last edited Sep 2, 2010 at 3:51 PM by SteveGuidi, version 7

Comments

No comments yet.