title: How to combine collection and property assertions using fluent-assertions?
body: <p>I would like to "combine" Fluent Assertion's collection assertions and property assertions, e.g. assert that two <code>IEnumerable</code>'s are pairwise-equal using property-by-property (possibly "nested") comparison (i.e. structural equality, in functional language parlance).</p>
  
  <p>Concrete example:</p>
  
  <pre><code>var dic = new Dictionary&lt;int, string&gt;() { {1, "hi"}, {2, "bye" } };
  var actual = dic.ToSelectListItems(0).OrderBy(si =&gt; si.Text);
  
  var expected = new List&lt;SelectListItem&gt;() {
      new SelectListItem() {Selected = false, Text="bye", Value="2"},
      new SelectListItem() {Selected = false, Text="hi", Value="1"}
  };
  </code></pre>
  
  <p>Here I wrote an extension method <code>ToSelectListItems</code> that converts a <code>Dictionary</code> to an <code>IEnumerable</code> of <code>SelectListItem</code>s (from ASP.NET MVC). I want to assert that <code>actual</code> and <code>expected</code> are "structurally" equal, noting that the reference type <code>SelectListItem</code> does not override <code>Equal</code>s and thus uses reference equality by default.</p>
  
  <p><strong>Update</strong></p>
  
  <p>Currently using the following hand-rolled solution, still hoping for something better built into FluentAssertions:</p>
  
  <pre><code>public static void ShouldBeStructurallyEqualTo&lt;T, U&gt;(this IEnumerable&lt;T&gt; actual, IEnumerable&lt;U&gt; expected) {
      actual.Should().HaveCount(expected.Count());
      actual.Zip(expected).ForEach(pair =&gt; pair.Item1.ShouldHave().AllProperties().IncludingNestedObjects().EqualTo(pair.Item2));
  }
  </code></pre>
  
  <p>(note: <code>Zip</code> here is my own <code>IEnumerable</code> extention which uses <code>Tuple.Create</code> as the default projection)</p>
  
  <p><strong>Update 2</strong></p>
  
  <p>Here are two minimal examples:</p>
  
  <pre><code>public class FooBar {
      public string Foo { get; set; }
      public int Bar { get; set; }
  }
  
  public class TestClass {
      [Test]
      public void MinimalExample() {
          List&lt;FooBar&gt; enumerable1 = new List&lt;FooBar&gt;() { new FooBar() { Foo = "x", Bar = 1 }, new FooBar() { Foo = "y", Bar = 2 } };
          List&lt;FooBar&gt; enumerable2 = new List&lt;FooBar&gt;() { 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&lt;FooBar&gt; enumerable1 = (new List&lt;FooBar&gt;() { new FooBar() { Foo = "x", Bar = 1 }, new FooBar() { Foo = "y", Bar = 2 } }).Cast&lt;FooBar&gt;();
          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()
      }
  }
  </code></pre>