How to use FluentAssertions to test for the fact that all properties should have been modified without having any known/fixed target property values

350 views Asked by At

I want to use FluentAssertion's object graph comparison to assert the fact that all properties have been changed/modified to any value I don't care about the target value. Actually, to highlight that, let's say I do not even have access to the target object, i.e. I don't know nor care about what the result is exactly - except that it should be different than what it was before (respectively in some other state, e.g. than what it was when it was empty).

Use case/background

The use case is a simple mapping code from one class to another, but I do not want to duplicate the actual mapping results nor do I want to hardcode example versions of the whole mapping into the test. A simple test that all properties are updated (yes to any value and yes I know they could be mixed-up etc. and my test then does not protect against that) should be enough for my use case. The main thing I want to catch is properties being forgotten or removed in the mapping, not mapping to wrong property names or so. (This is more easily being caught in code-reviews e.g. than forgotten properties.)

In the code below I just use a little simplified version, where I have a system under test (SUT) that updates all properties. In the real code, I would e.g. use a after.Should().NotBeEquivalentTo(new MyClass2()); to check that an object is not empty. Actually, this may be a very similar use case: checking all properties of the object have been filled.

For what's it worth, comparing if something (i.e.all properties) has/have not been changed is pretty straightforward. You can just do a after.Should().BeEquivalentTo(before);, as in such a case, you basically also know what properties to expect, i.e. the target property values are the same as the one from the before object/state, so that is easy. The inverse way I want to do here is not, though...

Code

Here is a simple example, where of course after would be provided by the SUT:

    public class ExampleTests
    {
        private class MyClass
        {
            public int Apple;
            public int Banana;
        }

        private class MyClass2
        {
            public int Apple;
            public int Banana;
        }

        [Test]
        public void ShouldFail_BecauseClassesAreEquivalent()
        {
            var before = new MyClass { Apple = 1, Banana = 2 };
            var after = new MyClass2 { Apple = 1, Banana = 2 };

            after.Should().NotBeEquivalentTo(before); // test fails (OK)
        }

        [Test]
        public void ShouldFail_BecauseClassesShouldHaveALlPropertiesModified_NotOnlyOne()
        {
            var before = new MyClass { Apple = 1, Banana = 2 };
            var after = new MyClass2 { Apple = 100, Banana = 2 };

            after.Should().NotBeEquivalentTo(before); // test succeeds (NOT OK)
        }

        [Test]
        public void ShouldWork_BecauseClassesDoHaveALlPropertiesModified()
        {
            var before = new MyClass { Apple = 1, Banana = 2 };
            var after = new MyClass2 { Apple = 100, Banana = 200 };

            after.Should().NotBeEquivalentTo(before); // test succeeds (OK)
        }
    }

Tries

As you can see I tried it with NotBeEquivalentTo which looks reasonable, however, it fails for the case where only one property is modified. The reason is simple: The object it actually different if you think about the whole object. However, in my case, I only consider it to be different, when all attributes have been changed.

How can I actually achieve that?

2

There are 2 answers

2
Sergey K On

If I understood correctly your problem you want to be able to compare to objects and they are different objects only if all properties changed. so the problem is not how you will test it but in implementation of your classes. You need to override equality methods in your classes. or you need to create you custom comparer

0
Tao Gómez Gil On

I have not found how to do this natively with FluentAssertions, but you could do something like this yourself:

[Test]
public void ShouldFail_BecauseClassesShouldHaveALlPropertiesModified_NotOnlyOne()
{
    var before = new MyClass { Apple = 1, Banana = 2 };
    var after = new MyClass2 { Apple = 100, Banana = 2 };

    AssertAllPropertiesAreDifferent(before, after); // test succeeds (NOT OK)
}

private static void AssertAllPropertiesAreDifferent(MyClass before, MyClass2 after)
{
    var beforeFields = before.GetType().GetFields();
    var afterFields = after.GetType().GetFields();

    foreach (var beforeField in beforeFields)
    {
        var afterField = afterFields.First(f => f.Name == beforeField.Name);

        var beforeValue = beforeField.GetValue(before);
        var afterValue = afterField.GetValue(after);

        beforeValue.Should().NotBe(afterValue, $"{afterField.Name} field had the same value");
    }
}

However be cautious, there are many things that you should take into account (properties and not only fields, private/protected fields and properties, missing members in one object...).