views:

177

answers:

7

I am doing some unit tests and I want to know if there's any way to test if a list is ordered by a property of the objects it contains.

Right now I am doing it this way but I don't like it, I want a better way. Can somebody help me please?

// (fill the list)
List<StudyFeedItem> studyFeeds = 
 Feeds.GetStudyFeeds(2120, DateTime.Today.AddDays(-200), 20);   
        StudyFeedItem previous = studyFeeds.First();

        foreach (StudyFeedItem item in studyFeeds)
        {
            if (item != previous)
            {
                Assert.IsTrue(previous.Date > item.Date);
            }

            previous = item;
        }
+1  A: 

One way or another you're going to have to walk the list and ensure that the items are in the order you want. Since the item comparison is custom, you could look into creating a generic method for this and passing in a comparison function - the same way that sorting the list uses comparison functions.

Steven M. Cherry
A: 
var studyFeeds = Feeds.GetStudyFeeds(2120, DateTime.Today.AddDays(-200), 20);
var orderedFeeds = studyFeeds.OrderBy(f => f.Date);

for (int i = 0; i < studyFeeds.Count; i++)
{
    Assert.AreEqual(orderedFeeds[i].Date, studyFeeds[i].Date);
}
Darin Dimitrov
+3  A: 
if(studyFeeds.Length < 2)
  return;

for(int i = 1; i < studyFeeds.Length;i++)  
 Assert.IsTrue(studyFeeds[i-1].Date > studyFeeds[i].Date);

for isn't dead just quite yet!

Will
+8  A: 

If your unit testing framework has helper methods to assert equality of collections, you should be able do something like this (NUnit flavored):

var sorted = studyFeeds.OrderBy(s => s.Date);
CollectionAssert.AreEqual(sorted.ToList(), studyFeeds.ToList());

The assert method works with any IEnumerable, but when both collections are of type IList or "array of something", the error message thrown when the assert fails will contain the index of the first out-of-place element.

Jørn Schou-Rode
+1. Nice. I've used NUnit for years and didn't know about this. Also just discovered FileAssert and StringAssert! Thanks.
RichardOD
it doesn't work :(. Error message:Baischana.Components.Services.Test.StudyFeedsTest.GetStudyFeeds_IsOrderedByDate: Expected: <System.Linq.OrderedEnumerable`2[Baischana.Components.StudyFeedItem,System.DateTime]> But was: < <Baischana.Components.StudyFeedItem>, <Baischana.Components.StudyFeedItem>, <Baischana.Components.StudyFeedItem>, <Baischana.Components.StudyFeedItem>, <Baischana.Components.StudyFeedItem> >
Nicole
Sorting a list is quite ineffective in terms of computational complexity.
el.pescado
+1 for CollectionAssert, I didn't know that. @el-pescado: that's true, but it's a unit test, not production code...
Thomas Levesque
@Nicole: That is the error message you get when the assertion fails. Try to pass the assert method two objects of type IList (my answer is edited to do that), and it should give you another error message, containing the index of the first out-of-place element.
Jørn Schou-Rode
A: 

Thanks! I only wanted to know if there was a direct way of knowing instead of having to walk the list comparing each objet with the next one. Tank you very much! Nicole

Nicole
Welcome to SO! use comments to reply to answers, not a new answer, kthx.
Will
+1  A: 

If you are using MSTest, you may want to take a look at CollectionAsser.AreEqual.

Enumerable.SequenceEqual may be another useful API to use in an assertion.

In both cases you should prepare a list that holds the expected list in the expected order, and then compare that list to the result.

Here's an example:

var studyFeeds = Feeds.GetStudyFeeds(2120, DateTime.Today.AddDays(-200), 20);   
var expectedList = studyFeeds.OrderByDescending(x => x.Date);
Assert.IsTrue(expectedList.SequenceEqual(studyFeeds));
Mark Seemann
THANK YOU SO MUCH!!!!!you saved my life :)thanks again.
Nicole
+2  A: 

A .NET 4.0 way would be to use the Enumerable.Zip method to zip the list with itself offset by one, which pairs each item with the subsequent item in the list. You can then check that the condition holds true for each pair, e.g.

var ordered = studyFeeds.Zip(studyFeeds.Skip(1), (a, b) => new { a, b })
                        .All(p => p.a.Date < p.b.Date);

If you're on an earlier version of the framework you can write your own Zip method without too much trouble, something like the following (argument validation and disposal of the enumerators if applicable is left to the reader):

public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
    this IEnumerable<TFirst> first,
    IEnumerable<TSecond> second,
    Func<TFirst, TSecond, TResult> selector)
{
    var e1 = first.GetEnumerator();
    var e2 = second.GetEnumerator();
    while (e1.MoveNext() & e2.MoveNext()) // one & is important
        yield return selector(e1.Current, e2.Current);
)
Greg Beech