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

Lately I’ve been trying to return IEnumerable<T> 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<T> 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<T> 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<T> 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<T> return type. Have your test code wrap the IEnumerable<T> in an IList<T>.

   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: }

 

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

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

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

   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;

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

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


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in .NET, C#, Pragmatism, Principles and Patterns, Unit Testing. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.lostechies.com/members/dahlbyk/default.aspx Keith Dahlby

    Good advise – in my view, IList<> should only be exposed if the collection needs to be modifiable.

    It doesn’t happen often, but in cases where the returned collection has to provide random access I like to explicitly return ReadOnlyCollection<> to make very explicit how the collection is expected to be used.

  • http://www.squaredroot.com Troy Goode

    Why not just take advantage of the built-in LINQ extension methods for IEnumerable? Specifically .Count() and .ElementAt(int)?

  • http://www.lostechies.com/members/derick.bailey/default.aspx derick.bailey

    @Troy,

    oh, sure… do it the EASY way…

    shows how much i pay attention to / use linq and extension methods, eh? :)

  • http://thelimberlambda.com/ Eric Smith

    I have to agree with @Troy – honestly I read your blog post and then promptly assumed that I wasn’t grokking the nub since … well, with extension methods you *do* have access to a) the number of elements in the enumeration and b) a particular element.

    Granted, not necessarily the most efficient access, but access none-the-less.

  • Peter Lehmann

    well i dont agree with this.
    IEnumerable don’t give the methods I need, like count, get by index i known this method are there with extension methods in the system.Linq.
    so in the end why not use IList and give a comment don’t use add or remove on the List but on the class?

  • Ant

    In tests, I typically call the .ToArray() extension method before asserting about the contents.

  • http://www.agilification.com Jeff Doolittle

    Peter –

    If users have to read the comments for the code to work correctly, I think you may have serious problems on your hands. I prefer code that speaks for itself over comments any day.

  • http://www.tavaresstudios.com Chris Tavares

    In general I agree with you, but unfortunately IEnumerable is missing an important semantic – are you allowed to enumerate multiple times? With every other collection, this is part of the contract, but with IEnumerable it’s not. We pretty much assume you can anyway, but you do need to be a little careful on this one.

  • Peter Lehmann

    @Jeff
    reading comment to known that not to do is not a good idea in general but, then it come to IList vs IEnumerable, here it is too big or too small to work. the best option will be a read only interface of IList without remove and add. so hide for the deplore he/she can make a error or for a count run tho a IEumerable for get a count. I personally like the first one, as long as there no read only interface that support this.
    NB: IEumerable do not have to have a end, what happed when you then try to get a count on that?

  • Erik

    Derick,
    There is also the Extension Method .ToList() if you really need the list operations in your test. It is also useful if you want to force enumeration rather than having it be lazy.

    i.e. someService.GetStuff().ToList();

    Erik

  • http://www.adverseconditionals.com Harry M

    Its also worth remembering that if you have a private List which you are exposing via IEnumerable you should write “return myPrivateList.ToList();” instead of just “return myPrivateList;” it or something, as the consumer can cast the IEnumerable back into a List and modify your private collection.

  • http://blog.markrendle.net/ Mark Rendle

    Peter: it’s a basic rule of OOP that you should expose the least-derived class or interface possible, and in the instance of read-only collections, that is IEnumerable. Consumers are free to call ToList or ToArray and then have whichever list construct they desire to count or index. And your argument about IEnumerable’s not having to end is spurious: how would you expose such a list as an IList?

    Harry M: a better method for creating a pure enumerable is

    myPrivateList.AsEnumerable();

  • Peter Lehmann

    @Mark
    I never said never use IEnumerable, what i said as if you expit the user to have Count then you need to use that interface that has that method in this sample IList.
    on that one i hope we can agree on.

    my other sample with a never ending IEnumerable I will never expose that as IList. as that first of all can’t be done (List was a intern array to keep), I used that as if you just cast a IEnumerable to IList when you are break the contact and hoping this can work.

  • mendicant

    I’m inclined to agree with the System.Linq suggestions. Also, in many cases, I don’t even care WHERE it is in the list, just that an item IS in the list. We have a few extension methods on IEnumerable such as

    .should_contain(item)

    beyond that, It’s mostly just .Count().should_be_equal_to(x) and such.

    I see the casting to a List as being overkill.

  • http://www.squaredroot.com Troy Goode

    @mendicant:

    Instead of writing your own “.should_contain(item)” I suggest using LINQ’s .Any(Func) like so:

    // make sure the list contains the number 99
    listOfNumbers.Any(num=> num == 99)

  • http://www.squaredroot.com Troy Goode

    In hindsight it looks like you’re using .should_contain as a Shoulda-style fluent BDDish syntax. I’d still throw my vote out there to at least use .Any(Func) in the underlying impelmentation of .should_contain(item).

  • Leyu Sisay

    Using Enumerable extensions and/or CollectionAssert makes the tests more readable & easy to work with IEnumerable’s

    Besides most of the extension methods on IEnumerable are well optimized. http://msmvps.com/blogs/jon_skeet/archive/2010/02/10/optimisations-in-linq-to-objects.aspx