Chciałbym "połączyć" asercje kolekcji i asercje własności, np. twierdzą, że dwa numery IEnumerable
są parami równymi, używając porównania właściwości według właściwości (prawdopodobnie "zagnieżdżonych") (to znaczy równości strukturalnej, w języku funkcjonalnym).Jak połączyć asercje kolekcji i własności za pomocą twierdzeń płynnych?
konkretny przykład:
var dic = new Dictionary<int, string>() { {1, "hi"}, {2, "bye" } };
var actual = dic.ToSelectListItems(0).OrderBy(si => si.Text);
var expected = new List<SelectListItem>() {
new SelectListItem() {Selected = false, Text="bye", Value="2"},
new SelectListItem() {Selected = false, Text="hi", Value="1"}
};
Tutaj napisałem metodę rozszerzenia ToSelectListItems
który konwertuje Dictionary
do IEnumerable
z SelectListItem
s (z ASP.NET MVC). Chcę twierdzić, że actual
i expected
są "strukturalnie" równe, zwracając uwagę, że typ odniesienia SelectListItem
nie zastępuje Equal
s i dlatego domyślnie używa równości odniesienia.
Aktualizacja
Obecnie stosując następujące ręcznie walcowane rozwiązanie, wciąż mając nadzieję na coś lepszego wbudowanym FluentAssertions:
public static void ShouldBeStructurallyEqualTo<T, U>(this IEnumerable<T> actual, IEnumerable<U> expected) {
actual.Should().HaveCount(expected.Count());
actual.Zip(expected).ForEach(pair => pair.Item1.ShouldHave().AllProperties().IncludingNestedObjects().EqualTo(pair.Item2));
}
(uwaga: Zip
Oto mój własny IEnumerable
rozbudowa który wykorzystuje Tuple.Create
jako domyślna projekcja)
Aktualizacja 2
Oto dwa przykłady: minimalne
public class FooBar {
public string Foo { get; set; }
public int Bar { get; set; }
}
public class TestClass {
[Test]
public void MinimalExample() {
List<FooBar> enumerable1 = new List<FooBar>() { new FooBar() { Foo = "x", Bar = 1 }, new FooBar() { Foo = "y", Bar = 2 } };
List<FooBar> enumerable2 = new List<FooBar>() { new FooBar() { Foo = "x", Bar = 1 }, new FooBar() { Foo = "y", Bar = 2 } };
enumerable1.ShouldHave().SharedProperties().IncludingNestedObjects().EqualTo(enumerable2);
//Test 'TestClass.MinimalExample' failed: System.Reflection.TargetParameterCountException : Parameter count mismatch.
// at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
// at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
// at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
// at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
// at FluentAssertions.Assertions.PropertyEqualityValidator.AssertSelectedPropertiesAreEqual(Object subject, Object expected)
// at FluentAssertions.Assertions.PropertyEqualityValidator.Validate(UniqueObjectTracker tracker, String parentPropertyName)
// at FluentAssertions.Assertions.PropertyEqualityValidator.Validate()
// at FluentAssertions.Assertions.PropertyAssertions`1.EqualTo(Object otherObject, String reason, Object[] reasonArgs)
// at FluentAssertions.Assertions.PropertyAssertions`1.EqualTo(Object otherObject)
// MiscAssertions.cs(32,0): at TestClass.MinimalExample()
}
[Test]
public void MinimalExample2() {
IEnumerable<FooBar> enumerable1 = (new List<FooBar>() { new FooBar() { Foo = "x", Bar = 1 }, new FooBar() { Foo = "y", Bar = 2 } }).Cast<FooBar>();
FooBar[] enumerable2 = new [] { new FooBar() { Foo = "x", Bar = 1 }, new FooBar() { Foo = "y", Bar = 2 } };
enumerable1.ShouldHave().SharedProperties().IncludingNestedObjects().EqualTo(enumerable2);
//Test 'TestClass.MinimalExample2' failed: System.InvalidOperationException : Please specify some properties to include in the comparison.
// at FluentAssertions.Assertions.PropertyEqualityValidator.Validate(UniqueObjectTracker tracker, String parentPropertyName)
// at FluentAssertions.Assertions.PropertyEqualityValidator.Validate()
// at FluentAssertions.Assertions.PropertyAssertions`1.EqualTo(Object otherObject, String reason, Object[] reasonArgs)
// at FluentAssertions.Assertions.PropertyAssertions`1.EqualTo(Object otherObject)
// MiscAssertions.cs(52,0): at TestClass.MinimalExample2()
}
}
Naprawiłem obejście Stephensa, aby pracować z wartościowymi typami, jeśli T było typem wartości, nie działało bez tej poprawki: https://gist.github.com/1877672 –
Jest w publicznej wersji beta wersji 2.0 http: : //fluentassertions.codeplex.com/releases/view/82423 –
Jak to jest obsługiwane? Nie mogę znaleźć przykładu dokumentacji. – ferpega