Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FluentAssertions - how make ShouldBeEquivalentTo compare empty and null as equal

I am using Fluent Assertion library as part of my unit tests for some custom serialization code and I am looking for a way to force ShouldBeEquivalentTo to compare as equal a null and empty list.

Basically, my tests look something like:

    [Test]
    public void Should_be_xxx()
    {
        ClassWithList one = new ClassWithList { Id = "ten", Items = null };

        string serialized = Serialize(one);
        ClassWithList two = Deserialize(serialized);

        two.ShouldBeEquivalentTo(one);
    }

However, one of the features of the Deserialize method is that if a collection type is missing in the input data, it sets the property on the deserialized class to an empty list, rather than null. So, very simplified, I end up with a situation where in instance two, Items = new List<string> rather than null.

Obviously, I could set one.Items = new List<string>() before comparing, but in reality I have a large number of complex domain objects that I am asserting in these methods and I am looking for a general solution. To put it another way, does anyone know how to make the following test pass:

    public class ClassWithList
    {
        public string Id { get; set; }
        public List<string> Items { get; set; }
    }

    [Test]
    public void Should_be_xxx()
    {
        ClassWithList one = new ClassWithList { Id = "ten", Items = null };
        ClassWithList two = new ClassWithList { Id = "ten", Items = new List<string>() };

        two.ShouldBeEquivalentTo(one);
    }

To put it another way, I am looking to apply the following test to all collections in a class X as part of comparing equivalence:

  if (subject.Items == null)
  {
     expected.Items.Should().BeEmpty();
  }
  else
  {
     expected.Items.Should().BeEquivalentTo(subject.Items);
  }
like image 386
Mike Sackton Avatar asked Aug 22 '16 16:08

Mike Sackton


3 Answers

Based on the information from Dennis above, I was able to solve this will the following actual code:

    public class ClassWithList
    {
        public string Id { get; set; }
        public List<string> Items { get; set; }
        public List<ClassWithList> Nested { get; set; }
    }

    [TestClass]
    public class Test
    {
        [TestMethod]
        public void Should_compare_null_to_empty()
        {
            ClassWithList one = new ClassWithList { Id = "ten", Items = null, Nested = new List<ClassWithList> { new ClassWithList { Id = "a" } } };
            ClassWithList two = new ClassWithList { Id = "ten", Items = new List<string>(), Nested = new List<ClassWithList> { new ClassWithList { Id = "a", Items = new List<string>(), Nested = new List<ClassWithList> { } } } };

            two.ShouldBeEquivalentTo(one, opt => opt
                .Using<IEnumerable>(CheckList)
                .When(info => typeof(IEnumerable).IsAssignableFrom(info.CompileTimeType)));
        }

        private void CheckList(IAssertionContext<IEnumerable> a)
        {
            if (a.Expectation == null)
            {
                a.Subject.Should().BeEmpty();
            }
            else
            {
                a.Subject.ShouldBeEquivalentTo(a.Expectation, opt => opt
                    .Using<IEnumerable>(CheckList)
                    .When(info => typeof(IEnumerable).IsAssignableFrom(info.CompileTimeType)));
            }
        }
    }
like image 51
Mike Sackton Avatar answered Sep 18 '22 07:09

Mike Sackton


You'll have to implement a custom 'IEquivalencyStep' or u.se 'options.Using(custom action).WhenTypeIs(predicate).

like image 44
Dennis Doomen Avatar answered Sep 18 '22 07:09

Dennis Doomen


Create an IAssertionRule:

public class EnumerableNullEmptyEquivalenceRule : IAssertionRule
{
    public bool AssertEquality(IEquivalencyValidationContext context)
    {
        // not applicable - return false
        if (!typeof(IEnumerable).IsAssignableFrom(context.SelectedMemberInfo.MemberType)) return false;

        return context.Expectation == null && ((IEnumerable)context.Subject).IsNullOrEmpty();
    }
}

Then apply to your BeEquivalentTo call:

actual.Should().BeEquivalentTo(expected, opt => opt.Using(new EnumerableNullEmptyEquivalenceRule()));
like image 36
Kai G Avatar answered Sep 18 '22 07:09

Kai G