1

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?

rklec
  • 73
  • 10
  • I little warning here: you seem to say that you would use FluentAssertions in "real code", not just for tests. I'm not sure that's a good idea, this library is meant for testing. – Tao Gómez Gil Jul 12 '23 at 16:12
  • 1
    @TaoGómezGil No with "real code" I mean the test code in comparison to the MVP I gave above. – rklec Jul 12 '23 at 16:44

2 Answers2

1

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...).

Tao Gómez Gil
  • 2,228
  • 20
  • 36
0

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

Sergey K
  • 4,071
  • 2
  • 23
  • 34
  • Yeah, if both classes implement `Equal` methods then I could use the `.NotBe` method of FluentAssertions. However, when it is different classes that may be tricky and you have to do it just for this test, whereas `BeEquivalentTo` of FluentAssertions usually is so convenient and can compare all properties out-of-the-box - I don't really need special/fancy comparison mechanisms implemented in `Equal`... So this would be a little cumbersome IMHO. – rklec Jul 12 '23 at 16:48
  • @rklec yes you need to implement base abstract class and ovveride equals and gethashcode functions and then derive your classes from it! one more time the problem is not in FluentAssersion but in your code design and what you gonna acheive! One question to you in what case they two objects are equals ? – Sergey K Jul 12 '23 at 19:07