66

私はいくつかの単体テストを行っていますが、リストが含まれているオブジェクトのプロパティによって並べ替えられているかどうかをテストする方法があるかどうかを知りたいです。

今はこうやってやっていますが、嫌いです。もっといい方法が欲しいです。誰か助けてくれませんか?

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

22 に答える 22

70

MSTestを使用している場合は、CollectionAssert.AreEqualを確認することをお勧めします。

Enumerable.SequenceEqualは、アサーションで使用するもう1つの便利なAPIです。

どちらの場合も、期待されるリストを期待される順序で保持するリストを作成してから、そのリストを結果と比較する必要があります。

次に例を示します。

var studyFeeds = Feeds.GetStudyFeeds(2120, DateTime.Today.AddDays(-200), 20);   
var expectedList = studyFeeds.OrderByDescending(x => x.Date);
Assert.IsTrue(expectedList.SequenceEqual(studyFeeds));
于 2009-12-21T14:03:15.363 に答える
52

.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);

フレームワークの以前のバージョンを使用している場合は、次のような独自の 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);
}
于 2009-12-21T14:04:40.100 に答える
27

単体テストフレームワークにコレクションの同等性を表明するヘルパーメソッドがある場合は、次のようなことができるはずです(NUnitフレーバー):

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

assertメソッドは任意ので機能しますIEnumerableが、両方のコレクションがタイプIListまたは「何かの配列」である場合、assertが失敗したときにスローされるエラーメッセージには、最初のアウトオブプレース要素のインデックスが含まれます。

于 2009-12-21T14:02:50.107 に答える
20

リストの並べ替えを含む投稿されたソリューションは高価です-リストが並べ替えられているかどうかを判断するには、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

于 2016-01-11T11:47:29.530 に答える
11

Greg Beechの回答は優れていますが、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);

これにより、1 つのラムダ式と 1 つの匿名型を節約できます。

(私の意見では、匿名型を削除すると読みやすくなります。)

于 2015-09-15T05:54:32.787 に答える
9
if(studyFeeds.Length < 2)
  return;

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

forまだ死んでいない!

于 2009-12-21T14:00:21.857 に答える
7

どうですか:

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

ここで、はを実装yourComparerするインスタンスです。これにより、すべての要素が列挙内の次の要素よりも少なくなります。YourComparerIComparer<YourBusinessObject>

于 2009-11-04T19:57:58.347 に答える
6

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
}
于 2016-01-11T11:43:51.273 に答える
4

これが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;
    }

これが役に立てば幸いです。これを解決するのに何年もかかりました。

于 2011-10-26T15:58:17.110 に答える
4

次のような拡張メソッドを使用できます。

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

シーケンスがソートされているかどうかを確認し、方向も決定できます。

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
于 2016-01-11T11:49:56.650 に答える
2

シーケンスをチェックすると、4 つの異なる結果が生じる可能性があります。Sameシーケンス内のすべての要素が同じ (またはシーケンスが空) であることを意味します。

enum Sort {
  Unsorted,
  Same,
  SortedAscending,
  SortedDescending
}

シーケンスのソートを確認する方法は次のとおりです。

Sort GetSort<T>(IEnumerable<T> source, IComparer<T> comparer = null) {
  if (source == null)
    throw new ArgumentNullException(nameof(source));
  if (comparer == null)
    comparer = Comparer<T>.Default;

  using (var enumerator = source.GetEnumerator()) {
    if (!enumerator.MoveNext())
      return Sort.Same;
    Sort? result = null;
    var previousItem = enumerator.Current;
    while (enumerator.MoveNext()) {
      var nextItem = enumerator.Current;
      var comparison = comparer.Compare(previousItem, nextItem);
      if (comparison < 0) {
        if (result == Sort.SortedDescending)
          return Sort.Unsorted;
        result = Sort.SortedAscending;
      }
      else if (comparison > 0) {
        if (result == Sort.SortedAscending)
          return Sort.Unsorted;
        result = Sort.SortedDescending;
      }
    }
    return result ?? Sort.Same;
  }
}

foreachシーケンスの要素をペアとして調べる必要があるため、ループの代わりに列挙子を直接使用しています。コードはより複雑になりますが、効率も向上します。

于 2016-01-11T11:56:19.097 に答える
1

いずれにせよ、リストをたどって、アイテムが希望の順序になっていることを確認する必要があります。アイテムの比較はカスタムであるため、このためのジェネリックメソッドを作成し、比較関数を渡すことを検討できます。これは、リストの並べ替えで比較関数を使用するのと同じ方法です。

于 2009-12-21T13:59:19.677 に答える
1

何かLINQ-yは、別のソートされたクエリを使用することです...

var sorted = from item in items
 orderby item.Priority
 select item;

Assert.IsTrue(items.SequenceEquals(sorted));

型推論は、あなたが必要とすることを意味します

 where T : IHasPriority

ただし、同じ優先度のアイテムが複数ある場合、単体テストのアサーションでは、ジェイソンが提案したように、リスト インデックスをループするだけでよいでしょう。

于 2009-11-04T20:14:43.460 に答える
1

最初にリストの順序付きバージョンと順序なしバージョンを作成できます。

var asc = jobs.OrderBy(x => x);
var desc = jobs.OrderByDescending(x => x);

元のリストを両方と比較します。

if (jobs.SequenceEqual(asc) || jobs.SequenceEquals(desc)) // ...
于 2016-01-11T11:47:14.037 に答える
0
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);
}
于 2009-12-21T13:59:22.593 に答える
0

これは、より軽量な汎用バージョンです。降順でテストするには、>= 0 の比較を <= 0 に変更します。

public static bool IsAscendingOrder<T>(this IEnumerable<T> seq) where T : IComparable<T>
{
    var predecessor = default(T);
    var hasPredecessor = false;

    foreach(var x in seq)
    {
        if (hasPredecessor && predecessor.CompareTo(x) >= 0) return false;
        predecessor = x;
        hasPredecessor = true;
    }

    return true;
}

テスト:

  • new int[] { }.IsAscendingOrder() はtrueを返します
  • new int[] { 1 }.IsAscendingOrder() はtrueを返します
  • new int[] { 1,2 }.IsAscendingOrder() はtrueを返します
  • new int[] { 1,2,0 }.IsAscendingOrder() はfalse を返します
于 2015-10-18T22:25:47.040 に答える
0
Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual(
  mylist.OrderBy((a) => a.SomeProperty).ToList(),
  mylist,
  "Not sorted.");
于 2015-04-09T13:43:46.400 に答える