DEV Community

Cover image for MSTest CollectionAssert class - an overview
Davide Bellone
Davide Bellone

Posted on • Originally published at code4it.dev

MSTest CollectionAssert class - an overview

This is the third part of our journey. In the first part we listed the methods belonging to the Assert class, while in the second part we had a look at the StringAssert class.

Now we'll deep dive into the CollectionAssert class, which is a good fit for ensuring that a collection of elements follows desired specifications.

Introduction to CollectionAssert

This class tests various conditions associated with collections, like tests about the type of the elements. It is important to say that this class does not check if every element in the collection follows a certain rule (like "The ID must be greater than 0"), but it's more about high level checks.

For these examples I'll reuse two classes created for the first article of this series: User and AdminUser: the second class extends the first one:

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
}

public class AdminUser : User
{
    public string Department { get; set; }
}

Enter fullscreen mode Exit fullscreen mode

CollectionAssert.AllItemsAreInstancesOfType

With this method we can check if all the elements belonging to a collection are of the same type.

A simple example could be the one with a list of strings.

[TestMethod]
public void AllItemsAreInstanceOfType_String()
{
    string[] stringArray = new string[] { "Hello", "darkness", "my", "old", "friend" };

    CollectionAssert.AllItemsAreInstancesOfType(stringArray, typeof(string));
}
Enter fullscreen mode Exit fullscreen mode

Of course all the elements in the stringArray array are all strings, so it's easy to guess that the test will pass.

What about objects? In the next test I'll create a List of User elements, but with also an AdminUser object.
You should remember that AdminUser is also a User, but the vice versa is not true.

[TestMethod]
public void AllItemsAreInstanceOfType_AreAllUsers()
{
    List<User> list = new List<User>()
    {
        new User(){ Username = "Elvis", Id=1},
        new User(){ Username="Janis", Id=2},
        new AdminUser(){ Username="Freddie", Id=3, Department="Legends"}
    };

    CollectionAssert.AllItemsAreInstancesOfType(list, typeof(User)); //Pass
    CollectionAssert.AllItemsAreInstancesOfType(list, typeof(AdminUser)); //Fail
}
Enter fullscreen mode Exit fullscreen mode

The first test will pass, but the second one will fail because the object with Username = "Freddie" is the only object of type AdminUser.

CollectionAssert.AllItemsAreNotNull

This method is pretty straightforward: if an element in the collection is null it will fail.

[TestMethod]
public void AllItemsAreNotNull()
{
    List<User> list = new List<User>() {
        new User(){Username= "Elvis", Id=1},
        null,
        new AdminUser(){ Username="Freddie", Id=3, Department="Legends"}
    };

    CollectionAssert.AllItemsAreNotNull(list);
}

Enter fullscreen mode Exit fullscreen mode

Of course the test above will fail.

CollectionAssert.AllItemsAreUnique

The name is explicative.

The test below will pass:

[TestMethod]
public void AreAllItemsUnique_integers()
{
    int[] array = new int[] { 1, 2, 3, 9, 7 };
    CollectionAssert.AllItemsAreUnique(array);
}
Enter fullscreen mode Exit fullscreen mode

But life is not meant to be lived with only integers.
And that's where things become a bit harder.

[TestMethod]
public void AreAllItemsUnique_Users()
{
    User[] array = new User[] {
        new User(){Username= "Elvis", Id=1},
        new User(){Username= "Elvis", Id=1}
    };
    CollectionAssert.AllItemsAreUnique(array);
}
Enter fullscreen mode Exit fullscreen mode

Well, Elvis will be unique forever... but not here! The array variable contains two different instances of User, even if the properties have the same value. So this test will fail.

Digging a bit more into the AllItemsAreUnique class we can see that internally it works with dictionaries:

public static void AllItemsAreUnique(ICollection collection, string message, params object[] parameters)
{
    Assert.CheckParameterNotNull(collection, "CollectionAssert.AllItemsAreUnique", "collection", string.Empty);
    message = Assert.ReplaceNulls(message);
    bool flag = false;
    Dictionary<object, bool> dictionary = new Dictionary<object, bool>();
    foreach (object item in collection)
    {
        if (item == null)
        {
            if (!flag)
            {
                flag = true;
            }
            else
            {
                string message2 = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AllItemsAreUniqueFailMsg, new object[2]
                {
                    (message == null) ? string.Empty : message,
                    FrameworkMessages.Common_NullInMessages
                });
                Assert.HandleFail("CollectionAssert.AllItemsAreUnique", message2, parameters);
            }
        }
        else if (dictionary.ContainsKey(item))
        {
            string message3 = string.Format(CultureInfo.CurrentCulture, FrameworkMessages.AllItemsAreUniqueFailMsg, new object[2]
            {
                (message == null) ? string.Empty : message,
                Assert.ReplaceNulls(item)
            });
            Assert.HandleFail("CollectionAssert.AllItemsAreUnique", message3, parameters);
        }
        else
        {
            dictionary.Add(item, value: true);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Ok, it's not so exciting, but I wanted to show you what's under the hood and how comparison is handled.

So, let's get back to our problem: how can we make that test pass?

We must override the Equals and the GetHashCode method. Even here I reuse the UpdatedUser class from the first article

public class UpdatedUser
{
    public int Id { get; set; }
    public string Username { get; set; }
    public override bool Equals(object obj)
    {
        return Id == ((User)obj).Id;
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}
Enter fullscreen mode Exit fullscreen mode

Now the test will pass.

CollectionAssert.AreEqual

If you want to check if two collections are identical, you can use AreEqual. This checks if the same elements are in the same position in both collections.

For example this test will pass:

[TestMethod]
public void AreEqual_Int()
{
    int[] array1 = new int[] { 1, 2, 3, 4, 5, 6 };
    int[] array2 = new int[] { 1, 2, 3, 4, 5, 6 };
    CollectionAssert.AreEqual(array1, array2); //OK
}
Enter fullscreen mode Exit fullscreen mode

But the following will fail:

[TestMethod]
public void AreEqual_DifferentPosition_Int()
{
    int[] array1 = new int[] { 1, 2, 3, 5, 4, 6 };
    int[] array2 = new int[] { 1, 2, 3, 4, 5, 6 };
    CollectionAssert.AreEqual(array1, array2); //KO
}

Enter fullscreen mode Exit fullscreen mode

As we saw before, with simple objects it doesn't work:

[TestMethod]
public void AreEqual_Objects()
{
    List<User> list = new List<User>()
    {
        new User(){ Username = "Elvis", Id=1},
        new User(){ Username="Janis", Id=2},
        new AdminUser(){ Username="Freddie", Id=3, Department="Legends"}
    };

    List<User> list2 = new List<User>()
    {
        new User(){ Username = "Elvis", Id=1},
        new User(){ Username="Janis", Id=2},
        new AdminUser(){ Username="Freddie", Id=3, Department="Legends"}
    };

    CollectionAssert.AreEqual(list, list2);
}
Enter fullscreen mode Exit fullscreen mode

You have to use the UpdatedUser class in order to make it work.

[TestMethod]
public void AreEqual_ObjectsWithOverride()
{
    List<UpdatedUser> list = new List<UpdatedUser>()
    {
        new UpdatedUser(){ Username = "Elvis", Id=1},
        new UpdatedUser(){ Username="Janis", Id=2}
    };

    List<UpdatedUser> list2 = new List<UpdatedUser>()
    {
        new UpdatedUser(){ Username = "Elvis", Id=1},
        new UpdatedUser(){ Username="Janis", Id=2}
    };

    CollectionAssert.AreEqual(list, list2);
}
Enter fullscreen mode Exit fullscreen mode

What about structs? Well, you can imagine, everything is fine.

[TestMethod]
public void AreEqual_Structs()
{
    List<Employee> list = new List<Employee>()
    {
        new Employee(){ Age = 11, Id=1},
        new Employee(){ Age= 92, Id=2}
    };

    List<Employee> list2 = new List<Employee>()
    {
        new Employee(){ Age = 11, Id=1},
        new Employee(){ Age= 92, Id=2}
    };

    CollectionAssert.AreEqual(list, list2);
}
Enter fullscreen mode Exit fullscreen mode

CollectionAssert.AreEquivalent

While AreEqual checks if two collections are identical, there is a more shallow way to test two collections. AreEquivalent controls if the same elements are in the two collections, regardless of their position.

[TestMethod]
public void AreEquivalent_Int()
{
    int[] array1 = new int[] { 1, 2, 3, 4, 5, 6 };
    int[] array2 = new int[] { 4, 5, 6, 1, 2, 3 };
    CollectionAssert.AreEquivalent(array1, array2); //OK
}
Enter fullscreen mode Exit fullscreen mode

CollectionAssert.Contains

Ok, this method is easy to guess why it is used for...

[TestMethod]
public void Contains_Int()
{
    int[] array1 = new int[] { 1, 2, 3, 4, 5, 6 };
    CollectionAssert.Contains(array1, 5); //OK
}
Enter fullscreen mode Exit fullscreen mode

CollectionAssert.IsSubsetOf

Even here, it's pretty easy.

[TestMethod]
public void IsSubsetOf_Int()
{

    int[] array1 = new int[] { 1, 2, 3, 4, 5, 6 };
    int[] subset = new int[] { 2, 3 };
    CollectionAssert.IsSubsetOf(subset, array1); //OK
}

Enter fullscreen mode Exit fullscreen mode

Wrapping up

We had a review of the CollectionAssert class. I think it is very useful for checking if a collection is "well formed", checking for the type of the content and for the presence of an item. But it would be better have also a method for checking if a custom condition is respected by all the elements of the collection, for example specifying a lambda expression to be evaluated for each element.

Conclusion

This is the end of this journey through pain and joy. As you can see, MSTest is fine for basic tests, but if you want something more complete I suggest to use other libraries.

My favorite, by now, is FluentAssertion. It replaces the Assert class with a syntax that allows you to create more complex tests. It is more readable (well, fluent) and a test looks like:

[TestMethod]
public void MustContain()
{

    int[] array = new int[] { 1, 2, 3, 4, 5 };

    array.Should().Contain(4);
}
Enter fullscreen mode Exit fullscreen mode

I think that MSTest is perfect if you are moving your first steps in the world of unit testing, but when the complexity grows you should consider adopting other frameworks.

Top comments (0)