如何检查列表是否已订购?

Nic*_*ole 55 c# unit-testing list html-lists

我正在进行一些单元测试,我想知道是否有任何方法可以测试列表是否按其包含的对象的属性排序.

现在我这样做,但我不喜欢它,我想要一个更好的方法.有人可以帮帮我吗?

// (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;
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ann 60

如果您使用的是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));
Run Code Online (Sandbox Code Playgroud)


Gre*_*ech 37

.NET 4.0的方法是使用该Enumerable.Zip方法将列表自身压缩为1,将每个项目与列表中的后续项目配对.然后,您可以检查每对的条件是否成立,例如

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

如果您使用的是早期版本的框架,您可以编写自己的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);
}
Run Code Online (Sandbox Code Playgroud)

  • 这将比使用 OrderBy() 的其他答案更具内存效率。 (2认同)
  • 这需要在上面。 (2认同)

Jør*_*ode 26

如果你的单元测试框架有辅助方法来断言集合的相等性,你应该能够做这样的事情(NUnit风格):

var sorted = studyFeeds.OrderBy(s => s.Date);
CollectionAssert.AreEqual(sorted.ToList(), studyFeeds.ToList());
Run Code Online (Sandbox Code Playgroud)

assert方法适用于任何类型IEnumerable,但是当两个集合都是类型IList或"某事物的数组"时,断言失败时抛出的错误消息将包含第一个不合适元素的索引.

  • CollectionAssert的+1,我不知道.@ el-pescado:这是真的,但这是一个单元测试,而不是生产代码...... (6认同)

mat*_*ttk 23

Nunit 2.5引入了CollectionOrderedContraint和一个很好的语法来验证集合的顺序:

Assert.That(collection, Is.Ordered.By("PropertyName"));
Run Code Online (Sandbox Code Playgroud)

无需手动订购和比较.

  • @ErikPhilips你现在可以这样做.https://github.com/nunit/nunit/issues/1553 (2认同)

Ric*_*ard 12

发布的涉及对列表进行排序的解决方案很昂贵 - 确定列表是否排序可以在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;
}
Run Code Online (Sandbox Code Playgroud)

IsOrderedDescending通过更改> 0为可以轻松实现相应的< 0.

  • 比对数据进行排序的方法要好得多(这让我的牙齿发痒)。 (2认同)

小智 9

if(studyFeeds.Length < 2)
  return;

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

for 还没死呢!


jas*_*son 7

怎么样:

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

where yourComparer实例在哪里YourComparer实现IComparer<YourBusinessObject>.这可确保每个元素都小于枚举中的下一个元素.


Ano*_*ken 7

Greg Beech的回答虽然很好,但可以通过在Zip中进行测试来进一步简化.所以代替:

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

你可以简单地做:

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

这为您节省了一个lambda表达式和一个匿名类型.

(在我看来,删除匿名类型也使其更容易阅读.)

  • @StackHola我认为你有一个拼写错误:`(b => true)`如果`b`为假或真,我将总是返回true,我认为你的意思是`(b => b)` (4认同)

Far*_*yev 6

Linq的答案是:

您可以使用SequenceEqual方法检查原始订单和订购的订单是否相同.

var isOrderedAscending = lJobsList.SequenceEqual(lJobsList.OrderBy(x => x));
var isOrderedDescending = lJobsList.SequenceEqual(lJobsList.OrderByDescending(x => x));
Run Code Online (Sandbox Code Playgroud)

不要忘记导入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
}
Run Code Online (Sandbox Code Playgroud)


Tim*_*ter 6

您可以使用这样的扩展方法:

public static System.ComponentModel.ListSortDirection? SortDirection<T>(this IEnumerable<T> items, Comparer<T> comparer = null)
{
    if (items == null) throw new ArgumentNullException("items");
    if (comparer == null) comparer = Comparer<T>.Default;

    bool ascendingOrder = true; bool descendingOrder = true;
    using (var e = items.GetEnumerator())
    {
        if (e.MoveNext())
        {
            T last = e.Current; // first item
            while (e.MoveNext())
            {
                int diff = comparer.Compare(last, e.Current);
                if (diff > 0)
                    ascendingOrder = false;
                else if (diff < 0)
                    descendingOrder = false;

                if (!ascendingOrder && !descendingOrder)
                    break;
                last = e.Current;
            }
        }
    }
    if (ascendingOrder)
        return System.ComponentModel.ListSortDirection.Ascending;
    else if (descendingOrder)
        return System.ComponentModel.ListSortDirection.Descending;
    else
        return null;
}
Run Code Online (Sandbox Code Playgroud)

它可以检查序列是否已排序并确定方向:

var items = new[] { 3, 2, 1, 1, 0 };
var sort = items.SortDirection();
Console.WriteLine("Is sorted? {0}, Direction: {1}", sort.HasValue, sort);
//Is sorted? True, Direction: Descending
Run Code Online (Sandbox Code Playgroud)