Don’t Expose IList Just So You Can Assert Against Its Contents


Lately I’ve been trying to return IEnumerable whenever I need a collection that will only be enumerated or databound to something. This prevents me from making changes to the collection outside the context of the collection’s parent entity. The problem with doing this is that I might need to write a unit test that looks for a specific item in the collection, checks the count of the collection or otherwise needs to do something that the IEnumerable interface doesn’t provide.

With tools like Resharper, It’s easy to change the return types of the methods that you’re getting the collection from and use an IList or some other collection type that allows you to get at the information I want. However, this can lead to broken encapsulation and other potential problems in code. After all, I wanted to keep the collection encapsulated within the parent entity which is why I chose to use the IEnumerable in the first place.

The good news is that there’s a super simple solution to this situation that does not require changing the IEnumerable return type. Have your test code wrap the IEnumerable in an IList.

   1: IEnumerable<MyObject> myEnumerator = someService.GetStuff();

   2: var myCollection = new List<MyObject>(myEnumerator);

   3:  

   4: [Test]

   5: public void my_test()

   6: {

   7:   myCollection.Count.ShouldBe(1);

   8:   myCollection[0].ShouldEqual(myObject);

   9:   //etc.

  10: }

</div> </div>

 

If you’re doing interaction testing with an interface and a mock object, where the interface receives an IEnumerable, you can still use this trick. For example, if I have this method on an interface defintion: </p>

   1: void ShowProductCodes(IEnumerable<Lookup> productCodes);

</div> </div>

I can grab the output of this method via a stub and convert it to an IList. Here’s one way to do it via RhinoMocks: </p>

   1: var view = Mock<IAssetClassificationView>();

   2: view.Stub(v => v.ShowProductCodes(Arg<IEnumerable<Lookup>>.Is.Anything))

   3:     .Callback((IEnumerable<Lookup> lookups) =>

   4:     {

   5:         DisplayedProductCodes = new List<Lookup>(lookups);

   6:         return true;

   7:     });

   8: return view;

</div> </div>

Line 5 wraps up the IEnumerable into an IList object, letting me test the contents/count/etc on the collection. </p>

Now you never need to worry about whether you can test the IEnumerable when you are passing it around in your code. Just wrap it in an IList at test time and call your tests the way you need to. </p>

How Ruby Taught Me To DRY Up My Code With Lambda Blocks