我正在做一些单元测试,我想知道是否有办法测试列表是否按其包含的对象的属性排序。
现在我这样做,但我不喜欢它,我想要一个更好的方法。有人可以帮帮我吗?
// (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;
}
答案 0 :(得分:54)
如果您使用的是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));
答案 1 :(得分:36)
.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);
}
答案 2 :(得分:26)
如果你的单元测试框架有辅助方法来断言集合的相等性,你应该可以做这样的事情(NUnit风格):
var sorted = studyFeeds.OrderBy(s => s.Date);
CollectionAssert.AreEqual(sorted.ToList(), studyFeeds.ToList());
assert方法适用于任何IEnumerable
,但当两个集合都是IList
类型或“某事物数组”时,断言失败时抛出的错误消息将包含第一个输出的索引场所元素。
答案 3 :(得分:22)
Nunit 2.5引入了CollectionOrderedContraint和一个很好的语法来验证集合的顺序:
Assert.That(collection, Is.Ordered.By("PropertyName"));
无需手动订购和比较。
答案 4 :(得分:11)
发布的涉及对列表进行排序的解决方案很昂贵 - 确定列表是否已排序可以在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
。
答案 5 :(得分:9)
if(studyFeeds.Length < 2)
return;
for(int i = 1; i < studyFeeds.Length;i++)
Assert.IsTrue(studyFeeds[i-1].Date > studyFeeds[i].Date);
for
尚未死!
答案 6 :(得分:7)
怎么样:
var list = items.ToList();
for(int i = 1; i < list.Count; i++) {
Assert.IsTrue(yourComparer.Compare(list[i - 1], list[i]) <= 0);
}
其中yourComparer
是实现IComparer<YourBusinessObject>
的YourComparer
实例。这可确保每个元素都小于枚举中的下一个元素。
答案 7 :(得分:7)
Greg Beech answer虽然很出色,但可以通过在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);
这为您节省了一个lambda表达式和一个匿名类型。
(在我看来,删除匿名类型也更容易阅读。)
答案 8 :(得分: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
}
答案 9 :(得分: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;
}
希望这会有所帮助,花了我很多时间来完成这项工作。
答案 10 :(得分: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
答案 11 :(得分:2)
检查序列可能有四种不同的结果。 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
循环,因为我需要检查序列的元素作为对。它使代码更复杂,但也更有效。
答案 12 :(得分:1)
LINQ-y将使用单独的排序查询...
var sorted = from item in items
orderby item.Priority
select item;
Assert.IsTrue(items.SequenceEquals(sorted));
类型推断意味着您需要
where T : IHasPriority
但是,如果您有多个具有相同优先级的项目,那么对于单元测试断言,您可能最好只使用Jason建议的列表索引循环。
答案 13 :(得分:1)
您必须以某种方式走在列表中并确保项目符合您的要求。由于项目比较是自定义的,您可以考虑为此创建一个通用方法并传入比较函数 - 与排序列表使用比较函数的方式相同。
答案 14 :(得分:0)
您可以在扩展程序中使用lambda:
public static bool IsAscending<T>(this IEnumerable<T> self, Func<T, T, int> compareTo) {
var list = self as IList<T> ?? self.ToList();
for (int i = 1; i < list.Count; i++) {
if (compareTo(list[i - 1], list[i]) > 0) {
return false;
}
}
return true;
}
使用:
bool result1 = Enumerable.Range(2, 10).IsAscending((a, b) => a.CompareTo(b));
var lst = new List<(int, string)> { (1, "b"), (2, "a"), (3, "s1"), (3, "s") };
bool result2 = lst.IsAscending((a, b) => {
var cmp = a.Item1.CompareTo(b.Item1);
if (cmp != 0) {
return cmp;
} else {
return a.Item2.CompareTo(b.Item2);
}
});
答案 15 :(得分:0)
您可以先创建列表的有序版本和无序版本:
var asc = jobs.OrderBy(x => x);
var desc = jobs.OrderByDescending(x => x);
现在将原始列表与两者进行比较:
if (jobs.SequenceEqual(asc) || jobs.SequenceEquals(desc)) // ...
答案 16 :(得分:0)
虽然AnorZaken和Greg Beech的答案非常好,因为他们不需要使用扩展方法,但有时候避免使用Zip()会很好,因为有些枚举可能会以这种方式进行枚举。
可以在Aggregate()
中找到解决方案clearRect()
上述解决方案有点丑陋,是双重比较。像这样的浮动比较让我感到不安,因为它几乎就像一个浮点平等比较。但它似乎在这里工作了两倍。整数值也可以。 使用可空类型可以避免浮点比较,但是代码变得有点难以阅读。
double[] score1 = new double[] { 12.2, 13.3, 5, 17.2, 2.2, 4.5 };
double[] score2 = new double[] { 2.2, 4.5, 5, 12.2, 13.3, 17.2 };
bool isordered1 = score1.Aggregate(double.MinValue,(accum,elem)=>elem>=accum?elem:double.MaxValue) < double.MaxValue;
bool isordered2 = score2.Aggregate(double.MinValue,(accum,elem)=>elem>=accum?elem:double.MaxValue) < double.MaxValue;
Console.WriteLine ("isordered1 {0}",isordered1);
Console.WriteLine ("isordered2 {0}",isordered2);
答案 17 :(得分:0)
这是一个更轻量级的通用版本。要测试降序,请将&gt; = 0比较更改为&lt; = 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;
}
试验:
答案 18 :(得分:0)
Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual(
mylist.OrderBy((a) => a.SomeProperty).ToList(),
mylist,
"Not sorted.");
答案 19 :(得分:0)
这样的事情怎么样,没有对列表进行排序
public static bool IsAscendingOrder<T>(this IEnumerable<T> seq) where T : IComparable
{
var seqArray = seq as T[] ?? seq.ToArray();
return !seqArray.Where((e, i) =>
i < seqArray.Count() - 1 &&
e.CompareTo(seqArray.ElementAt(i + 1)) >= 0).Any();
}
答案 20 :(得分: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);
}