Effects Of Encapsulation On Unit Tests - EnumerableAssert

posted @ Thursday, October 11, 2007 1:12 PM

 

To keep your classes properly encapsulated, I've learned (from others and my own experience) that it's usually a good idea to expose collections only as IEnumerable<T>, until the need arises to elevate it to a higher type.  In keeping with this, it can sometimes make your unit tests less elegant.  Here are some examples and a quick little helper that can make things more readable...

So an extremely simple example would be something like this.

[Test]
public void Should_add_item_to_basket()
{
    IBasket basket = new Basket();
    IBasketItem basketItem = new BasketItem();
    
    basket.AddItemToBasket(basketItem);

    // TODO: Assert that the item was added to the basket
}

 

For reference, here is the IBasket interface:

public interface IBasket
{
    IEnumerable<IBasketItem> Items { get; }
    void AddItemToBasket(IBasketItem itemToAdd);
}

 

Ok, so of course there are a number of ways we could write this assertion.  Here are a couple examples using the out of the box MbUnit assertions.

Assert.IsTrue(new List<IBasketItem>(basket.Items).Contains(basketItem));

CollectionAssert.Contains(new List<IBasketItem>(basket.Items), basketItem);

foreach (IBasketItem currentItem in basket.Items) Assert.AreEqual(currentItem, basketItem);

 

Don't know about you, but those seem a little too verbose to me.  I tend to like something like this better.

EnumerableAssert.Contains(basket.Items, basketItem);

 

But you won't find that in the MbUnit framework.  Fortunately it's easy enough to write a little wrapper to "hide" the verbosity.

public class EnumerableAssert
{
    public static void Contains<T>(IEnumerable<T> enumerable, T actual)
    {
        CollectionAssert.Contains(new List<T>(enumerable), actual);
    }
}

 

Notice all I'm doing is leveraging one of MbUnit's existing assertions (CollectionAssert) to wrap an IEnumerable<T> and perform a contains assertion.  Pretty simple stuff, but it can help keep your tests more readable.

Comments
Andy Stopford - 10/11/2007 4:38 PM
# re: Effects Of Encapsulation On Unit Tests - EnumerableAssert
Hi Joey,

Great article, note that MbUnit has some basic support for generics in asserts

http://www.mertner.com/confluence/display/MbUnit/GenericAssert

No collection support at this moment but if you want to cook one up I'll welcome the patch any time :)

It is worth noting that MbUnit v3 squarely targets .NET 2.0 so will have much richer support for generics in its framework.

Andy
Tuna Toksoz - 10/11/2007 4:50 PM
# re: Effects Of Encapsulation On Unit Tests - EnumerableAssert
If you use c#3.0 or higher, you can take the advantage of extension methods. But with this, of course, you wont have the opportunity to have "EnumerableAssert" name, instead you'll have CollectionAssert with Contains(IEnumerable<T> enumerable, T actual) method
Joey Beninghove - 10/11/2007 5:19 PM
# re: Effects Of Encapsulation On Unit Tests - EnumerableAssert
Cool, thanks Andy. I totally missed that GenericAssert. Yeah, if I can put together some code for things I'd like to see in MbUnit, I'll definitely shoot a patch to you guys to check out.
Greg - 10/17/2007 11:29 PM
# re: Effects Of Encapsulation On Unit Tests - EnumerableAssert
Thanks for the tip.
I applied your concept to NUnit's "That" as follows:

public static void That<T>(IEnumerable<T> enumerable, Constraint constraint)
{
Assert.That(new List<T>(enumerable), constraint);
}

which now allows me to assert things like the following:
EnumerableAssert.That(enumerableListOfInts, Is.EquivalentTo(new int[]{1,2,3}));
EnumerableAssert.That(enumerableListOfInts, Is.SubsetOf(new int[] {1,2,3,4,5}));
EnumerableAssert.That(enumerableListOfInts, Has.Count(3));
Post Comment






Please add 1 and 7 and type the answer here: