Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Assert.AreEqual in MSTest - Done Right

DZone's Guide to

Assert.AreEqual in MSTest - Done Right

This unique solution is worth a look for testers who wish that Assert.AreEqual would let you change the way equality is checked.

· DevOps Zone ·
Free Resource

Download the blueprint that can take a company of any maturity level all the way up to enterprise-scale continuous delivery using a combination of Automic Release Automation, Automic’s 20+ years of business automation experience, and the proven tools and practices the company is already leveraging.

Every unit testing framework out there comes with a plethora of assertion methods - this is not necessarily a good thing. Instead of writing simple code, a developer is forced to choose the correct assertion method from a seemingly endless list of methods, most of which look exactly the same! Having multiple overloaded methods with the same name does not help at all, and sometimes the sheer amount of methods conceals the fact that the Assert you really need does not exist at all.

One of those so-called missing assertions is the one that enables a developer to compare two objects of the same type - by default most unit testing frameworks will use Equals method that in C#/Java will only check that the two instances point to the same reference.

Consider the following class:

public class MyClass
{
    public int MyInt { get; }
    public string MyString { get; }

    public MyClass(int myInt, string myString)
    {
        MyInt = myInt;
        MyString = myString;
    }

    public override string ToString()
    {
        return $"{nameof(MyInt)}: {MyInt}, {nameof(MyString)}: {MyString}";
    }
}

If we try to write the following test:

[TestMethod]
public void CheckForEquality()
{
    var myClass = new ClassUnderTest();
    var result = myClass.SomeMethodINeedToCheck();

    var expected = new MyClass(1, "some string");

    Assert.AreEqual(expected, result);
}

That test would always fail:

What I tend to recommend in this case is to override MyClass.Equals so that it would check the actual values instead of just a reference. Unfortunately, this solution leaves much to be desired - what happens if I want to compare different values in different tests? not to mention the fact that in some cases updating a class in your production code is not possible or desired. Another option is to use a 3rd party assertion libraries such as FluentAssertions or Shouldly - but I wish there was a simpler way...

My Solution***

What I would have liked is for Assert.AreEqual to let me change the way equality is checked.

Some frameworks enable developers to pass a comparer - a class that determines how another class will be tested for equality. But before you start bashing Microsoft know this: MSTest enables developers to pass a Comparer class as part of an assertion call - unfortunately that assertion is part of CollectionAssert which means that it could only be used when comparing two collections - or does it?

Using the magic of Extension methods I wrote the following method to compare two instances of MyClass:


public static void AreEqual<T>(this Assert assert, T expected, T actual, IComparer comparer)
{
    CollectionAssert.AreEqual(
        new[] { expected },
        new[] { actual }, comparer,
        $"\nExpected: <{expected}>.\nActual: <{actual}>.");
}

The reason I needed to add the third parameter (error description) is due to the fact that MSTest error would only tell us that index 0 does not equal and we want to see an error message that looks like this:

The test itself is quite trivial:


[TestMethod]
public void CheckForEquality()
{
    var myClass = new ClassUnderTest();
    var result = myClass.SomeMethodINeedToCheck();

    var expected = new MyClass(1, "some string1");

    Assert.That.AreEqual(expected, result, new MyClassComparer());
}

Notice the "Assert.That" in the last line? This method was created as an extensibility point - this is where we can hook Extensions methods just like the one we're using here.

Improving with Lambdas

The solution above solved the problem of comparing two instances using one assertion, which is nice. Unfortunately writing a new comparer each time is both repetitive (check that values of the same type, then check if one of them is not null), and error-prone (you should have checked for null before casting) and so I would have prefered to write the boilerplate code once and then only change the actual values that matters - the actual comparison.

And so I've written the following Comparer:


public delegate bool CompareFunc<in T>(T obj1, T obj2);

class LambdaComparer<T> : IComparer
{
    private readonly CompareFunc<T> _compareFunc;

    public LambdaComparer(CompareFunc<T> compareFunc)
    {
        _compareFunc = compareFunc;
    }

    public int Compare(object x, object y)
    {
        if (x == null && y == null)
        {
            return 0;
        }

        if (!(x is T t1) || !(y is T t2))
        {
            return -1;
        }

        return _compareFunc(t1, t2) ? 0 : 1;
    }
}

The class above enable just passing the "compare method" which makes simple tests even simpler - but there's more, by updating the extension method we can make the Comparer disappear:


public static void AreEqual<T>(this Assert assert, T expected, T actual, CompareFunc<T> compareFunc)
{
    var comparer = new LambdaComparer<T>(compareFunc);

    CollectionAssert.AreEqual(
        new[] { expected },
        new[] { actual }, comparer,
        $"\nExpected: <{expected}>.\nActual: <{actual}>.");
}

And now we can write the following test:


[TestMethod]
public void CheckForEqualityLambda()
{
    var myClass = new ClassUnderTest();
    var result = myClass.SomeMethodINeedToCheck();

    var expected = new MyClass(1, "some string1");

    Assert.That.AreEqual(expected, result,
        (myClass1, myClass2) =>
        myClass1.MyInt == myClass2.MyInt && myClass1.MyString == myClass2.MyString);
}

So there you have it - by using extension methods and the existing capabilities of MSTest we can have a simple and powerful assert and now instead of writing multiple assertions for a single result we can harness the methods above to test that the result is, in fact, equal to the expected value.

Download the ‘Practical Blueprint to Continuous Delivery’ to learn how Automic Release Automation can help you begin or continue your company’s digital transformation.

Topics:
devops ,testing ,unit testing ,java

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}