Как утверждать, что два списка содержат элементы с одинаковыми общедоступными свойствами в NUnit?
Я хочу утверждать, что элементы двух списков содержат значения, которые я ожидал, что-то вроде:
var foundCollection = fooManager.LoadFoo();
var expectedCollection = new List<Foo>()
{
new Foo() { Bar = "a", Bar2 = "b" },
new Foo() { Bar = "c", Bar2 = "d" }
};
//assert: I use AreEquivalent since the order does not matter
CollectionAssert.AreEquivalent(expectedCollection, foundCollection);
приведенный выше код не будет работать (я думаю, потому что .Метод Equals() не возвращает true для разных объектов с одинаковым значением). В моем тесте я забочусь только о значениях публичных свойств, а не о том, равны ли объекты. Что я могу сделать, чтобы сделать свое утверждение? 9 ответов
ПЕРЕРАБОТАННЫЙ ОТВЕТ
есть CollectionAssert.AreEqual(IEnumerable, IEnumerable, IComparer)
перегрузка утверждать, что две коллекции содержат одни и те же объекты в том же порядке, с помощью IComparer
реализация для проверки эквивалентности объектов.
в описанном выше сценарии, порядок не важен. Однако для достаточной обработки также ситуации, когда существует несколько эквивалентных объектов в двух коллекциях, необходимо сначала упорядочить объекты в каждой соберите и используйте сравнение один за другим, чтобы убедиться, что количество эквивалентных объектов одинаково в двух коллекциях.
Enumerable.OrderBy
обеспечивает перегрузку, которая принимает
нет, NUnit не имеет такого механизма, как в текущем состоянии. Вам придется свернуть свою собственную логику утверждения. Либо как отдельный метод, либо используя Has.All.Matches
:
Assert.That(found, Has.All.Matches<Foo>(f => IsInExpected(f, expected)));
private bool IsInExpected(Foo item, IEnumerable<Foo> expected)
{
var matchedItem = expected.FirstOrDefault(f =>
f.Bar1 == item.Bar1 &&
f.Bar2 == item.Bar2 &&
f.Bar3 == item.Bar3
);
return matchedItem != null;
}
Это, конечно, предполагает, что вы знаете все соответствующие свойства заранее (в противном случае,IsInExpected
придется прибегнуть к рефлексии) и что порядок элементов не имеет.
(и Ваше предположение было правильным, коллекция NUnit утверждает, что использует компараторы по умолчанию для типов, которые в большинстве случаев определяются пользователем будет объект ReferenceEquals
)
Использование Имеет.Все.Matches () очень хорошо работает для сравнения a нашел коллекцию ожидается коллекция. Однако нет необходимости определять предикат, используемый Has.Все.Matches () как отдельная функция. Для относительно простых сравнений предикат может быть включен как часть лямбда-выражения, например.
Assert.That(found, Has.All.Matches<Foo>(f =>
expected.Any(e =>
f.Bar1 == e.Bar1 &&
f.Bar2 == e.Bar2 &&
f.Bar3= = e.Bar3)));
Теперь, в то время как это утверждение гарантирует, что каждая запись в нашел коллекция также существует в ожидается коллекция, это не доказывает обратного, а именно, что каждая запись в ожидается коллекция содержится в нашел коллекция. Итак, когда важно знать, что нашел и ожидается contain семантически эквивалентны (т. е. содержат одинаковые семантически эквивалентные записи), мы должны добавить дополнительное утверждение.
самый простой вариант-добавить следующий.
Assert.AreEqual(found.Count() == expected.Count());
для тех, кто предпочитает более крупный молоток, вместо этого можно использовать следующее утверждение.
Assert.That(expected, Has.All.Matches<Foo>(e =>
found.Any(f =>
e.Bar1 == f.Bar1 &&
e.Bar2 == f.Bar2 &&
e.Bar3= = f.Bar3)));
используя первое утверждение выше в сочетании со вторым (предпочтительным) или третьим утверждением, мы теперь доказали, что две коллекции семантически одинаковы.
для выполнения операций эквивиланса на сложных типах необходимо реализовать IComaprable.
http://support.microsoft.com/kb/320727
в качестве альтернативы вы можете использовать рекурсивное отражение, что менее желательно.
один из вариантов-написать пользовательские ограничения для сравнения элементов. Вот хорошая статья на эту тему: http://www.davidarno.org/2012/07/25/improving-nunit-custom-constraints-with-syntax-helpers/
у меня была похожая проблема. Список участников, который содержит "комментаторы" и другие ppl... Я хочу получить все комментарии и из этого вывести создателей, но меня интересуют только уникальные создатели. Если кто-то создал 50 комментариев, я хочу, чтобы ее имя появилось только один раз. Поэтому я пишу тест, чтобы увидеть, что комментаторы int результат GetContributors ().
Я могу ошибаться, но я думаю, что ваш После (что я был после, когда я нашел этот пост), чтобы утверждать, что есть ровно по одному из каждого предмета в одной коллекции, найденного в другой коллекции.
Я решил это вот так:
Assert.IsTrue(commenters.All(c => actual.Count(p => p.Id == c.Id) == 1));
Если вы также хотите, чтобы результирующий список не содержал других элементов, чем ожидалось, вы можете просто сравнить длину списков..
Assert.IsTrue(commenters.length == actual.Count());
Я надеюсь, что это полезно, если это так, я был бы очень признателен если бы вы оценили мой ответ.
Я рекомендую не использовать отражение или что-нибудь сложное, он просто добавляет больше работы/обслуживания.
Сериализуйте объект (я рекомендую json) и сравните их. Я не уверен, почему вы возражаете против заказа, но я все равно рекомендую его, поскольку он сохранит пользовательские сравнения для каждого типа.
и он автоматически работает с объектами домена.
пример (SharpTestsEx для беглого)
using Newtonsoft.Json;
using SharpTestsEx;
JsonConvert.SerializeObject(actual).Should().Be.EqualTo(JsonConvert.SerializeObject(expected));
вы можете написать его как простые расширения и сделайте его более читаемым.
public static class CollectionAssertExtensions
{
public static void CollectionAreEqual<T>(this IEnumerable<T> actual, IEnumerable<T> expected)
{
JsonConvert.SerializeObject(actual).Should().Be.EqualTo(JsonConvert.SerializeObject(expected));
}
}
а затем, используя Ваш пример, назовите его так:
var foundCollection = fooManager.LoadFoo();
var expectedCollection = new List<Foo>()
{
new Foo() { Bar = "a", Bar2 = "b" },
new Foo() { Bar = "c", Bar2 = "d" }
};
foundCollection.CollectionAreEqual(foundCollection);
вы получите сообщение assert, как Итак:
...: "a","Bar2":"b"}, {"Bar":"d","Bar2": "d"}]
...: "a","Bar2":"b"}, {"Bar":"c","Bar2": "d"}]
...__________________^_____
простой код, объясняющий, как использовать IComparer
using System.Collections;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace CollectionAssert
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
IComparer collectionComparer = new CollectionComparer();
var expected = new List<SomeModel>{ new SomeModel { Name = "SomeOne", Age = 40}, new SomeModel{Name="SomeOther", Age = 50}};
var actual = new List<SomeModel> { new SomeModel { Name = "SomeOne", Age = 40 }, new SomeModel { Name = "SomeOther", Age = 50 } };
NUnit.Framework.CollectionAssert.AreEqual(expected, actual, collectionComparer);
}
}
public class SomeModel
{
public string Name { get; set; }
public int Age { get; set; }
}
public class CollectionComparer : IComparer, IComparer<SomeModel>
{
public int Compare(SomeModel x, SomeModel y)
{
if(x == null || y == null) return -1;
return x.Age == y.Age && x.Name == y.Name ? 0 : -1;
}
public int Compare(object x, object y)
{
var modelX = x as SomeModel;
var modelY = y as SomeModel;
return Compare(modelX, modelY);
}
}
}