Как проверить, заказан ли список? - PullRequest
52 голосов
/ 21 декабря 2009

Я выполняю некоторые модульные тесты и хочу узнать, есть ли способ проверить, упорядочен ли список по свойству объектов, которые он содержит.

Сейчас я делаю это так, но мне это не нравится, я хочу лучшего способа. Может кто-нибудь помочь мне, пожалуйста?

// (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;
}

Ответы [ 20 ]

54 голосов
/ 21 декабря 2009

Если вы используете MSTest, вы можете взглянуть на CollectionAssert.AreEqual .

Enumerable.SequenceEqual может быть другим полезным API для использования в утверждении.

В обоих случаях вы должны подготовить список, содержащий ожидаемый список в ожидаемом порядке, а затем сравнить этот список с результатом.

Вот пример:

var studyFeeds = Feeds.GetStudyFeeds(2120, DateTime.Today.AddDays(-200), 20);   
var expectedList = studyFeeds.OrderByDescending(x => x.Date);
Assert.IsTrue(expectedList.SequenceEqual(studyFeeds));
36 голосов
/ 21 декабря 2009

.NET 4.0 мог бы использовать метод Enumerable.Zip, чтобы сжать список со смещением на единицу, который соединяет каждый элемент с последующим элементом в списке. Затем вы можете проверить, что условие выполняется для каждой пары, например,

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

Если вы работаете с более ранней версией фреймворка, вы можете написать свой собственный метод Zip без особых проблем, что-то вроде следующего (проверка аргументов и удаление перечислителей, если применимо, предоставляется читателю):

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);
}
26 голосов
/ 21 декабря 2009

Если ваша инфраструктура модульного тестирования имеет вспомогательные методы для утверждения равенства коллекций, вы должны иметь возможность сделать что-то вроде этого (приправленный NUnit):

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

Метод assert работает с любым IEnumerable, но когда обе коллекции имеют тип IList или «массив чего-либо», сообщение об ошибке, выдаваемое при сбое подтверждения, будет содержать индекс первого неуместного элемент.

22 голосов
/ 23 октября 2013

Представлена ​​Nunit 2.5 CollectionOrderedContraint и приятный синтаксис для проверки порядка коллекции:

Assert.That(collection, Is.Ordered.By("PropertyName"));

Нет необходимости заказывать и сравнивать вручную.

11 голосов
/ 11 января 2016

Решения, размещенные с сортировкой списка, являются дорогостоящими - определить, можно ли отсортировать список, можно сделать за O (N). Вот метод расширения, который проверит:

public static bool IsOrdered<T>(this IList<T> list, IComparer<T> comparer = null)
{
    if (comparer == null)
    {
        comparer = Comparer<T>.Default;
    }

    if (list.Count > 1)
    {
        for (int i = 1; i < list.Count; i++)
        {
            if (comparer.Compare(list[i - 1], list[i]) > 0)
            {
                return false;
            }
        }
    }
    return true;
}

Соответствующий IsOrderedDescending может быть легко реализован путем изменения > 0 на < 0.

9 голосов
/ 21 декабря 2009
if(studyFeeds.Length < 2)
  return;

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

for еще не умерла!

7 голосов
/ 15 сентября 2015

Грег Бук ответ , хотя он и превосходен, его можно еще больше упростить, выполнив тест в самом Zip. Так что вместо:

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

Вы можете просто сделать:

var ordered = !studyFeeds.Zip(studyFeeds.Skip(1), (a, b) => a.Date < b.Date)
                        .Contains(false);

Что спасет вас от одного лямбда-выражения и одного анонимного типа.

(По моему мнению, удаление анонимного типа также облегчает чтение.)

7 голосов
/ 04 ноября 2009

Как насчет:

var list = items.ToList();
for(int i = 1; i < list.Count; i++) {
    Assert.IsTrue(yourComparer.Compare(list[i - 1], list[i]) <= 0);
} 

, где yourComparer - это экземпляр YourComparer, который реализует IComparer<YourBusinessObject>. Это гарантирует, что каждый элемент меньше следующего элемента в перечислении.

6 голосов
/ 11 января 2016

Ответ на основе Linq:

Вы можете использовать метод SequenceEqual, чтобы проверить, является ли оригинал и заказанный один и тот же или нет.

var isOrderedAscending = lJobsList.SequenceEqual(lJobsList.OrderBy(x => x));
var isOrderedDescending = lJobsList.SequenceEqual(lJobsList.OrderByDescending(x => x));

Не забудьте импортировать System.Linq пространство имен.

Дополнительно:

Я повторяю, что этот ответ основан на Linq, вы можете добиться большей эффективности, создав собственный метод расширения.

Кроме того, если кто-то все еще хочет использовать Linq и проверить, упорядочена ли последовательность в восходящем или нисходящем порядке, вы можете добиться чуть большей эффективности, например:

var orderedSequence = lJobsList.OrderBy(x => x)
                               .ToList();

var reversedOrderSequence = orderedSequence.AsEnumerable()
                                           .Reverse();

if (lJobsList.SequenceEqual(orderedSequence))
{
     // Ordered in ascending
}
else (lJobsList.SequenceEqual(reversedOrderSequence))
{
     // Ordered in descending
}
4 голосов
/ 26 октября 2011

Вот как я это делаю с Linq и я сопоставим, может быть, не самый лучший, но работает для меня, и это независимо от инфраструктуры тестирования.

Итак, звонок выглядит так:

    myList.IsOrderedBy(a => a.StartDate)

Это работает для всего, что реализует IComparable, поэтому нумерует строки и все, что наследует от IComparable:

    public static bool IsOrderedBy<T, TProperty>(this List<T> list, Expression<Func<T, TProperty>> propertyExpression) where TProperty : IComparable<TProperty>
    {
        var member = (MemberExpression) propertyExpression.Body;
        var propertyInfo = (PropertyInfo) member.Member;
        IComparable<TProperty> previousValue = null;
        for (int i = 0; i < list.Count(); i++)
        {
            var currentValue = (TProperty)propertyInfo.GetValue(list[i], null);
            if (previousValue == null)
            {
                previousValue = currentValue;
                continue;
            }

            if(previousValue.CompareTo(currentValue) > 0) return false;
            previousValue = currentValue;

        }

        return true;
    }

Надеюсь, это поможет, мне понадобилось много времени, чтобы разобраться с этим.

...