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

huangapple go评论72阅读模式
英文:

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

问题

我想使用FluentAssertion的对象图比较来断言这个事实:_所有_属性都已经被更改/修改为任何值,我不关心目标值是什么。
实际上,为了突出这一点,让我们说我甚至没有访问目标对象,即我不知道也不关心结果究竟是什么 - 除了它应该与之前的状态不同(或者在某些其他状态下不同,例如当它为空时不同)。

使用案例/背景

使用案例是从一个类到另一个类的简单映射代码,但我不想复制实际的映射结果,也不想硬编码整个映射的示例版本到测试中。一个简单的测试,验证所有属性是否已更新(是的,是任何值,我知道它们可能会混在一起,但是我的测试不会对此进行保护)对于我的使用案例足够了。我想要捕获的主要内容是在映射中遗漏或删除的属性,而不是映射到错误的属性名称等等(这在代码审查中更容易被发现,例如忘记的属性)。

在下面的代码中,我只是使用了一个简化版本,在其中有一个被测试的系统(SUT)更新了所有属性。在实际代码中,我可能会使用after.Should().NotBeEquivalentTo(new MyClass2());来检查对象不为空。实际上,这可能是一个非常相似的用例:检查对象的所有属性是否都已填充。

就其价值而言,比较是否有某些东西(即所有属性)没有被更改是相当简单的。你只需执行after.Should().BeEquivalentTo(before);,因为在这种情况下,你基本上也知道要期望哪些属性,即目标属性值与before对象/状态的属性值相同,所以这很容易。但我想要在这里做的逆向方式却不是这样...

代码

以下是一个简单的示例,当然after会由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); // 测试失败(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); // 测试成功(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); // 测试成功(OK)
    }
}

尝试

正如你所看到的,我尝试使用NotBeEquivalentTo,这看起来是合理的,但是对于只更改一个属性的情况,它会失败。原因很简单:如果考虑整个对象,实际上对象是不同的。
然而,在我的情况下,我只有当所有属性都已更改时才认为它是不同的。

我如何才能实现这一点?

英文:

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?

答案1

得分: 1

我尚未找到在FluentAssertions中如何以本地方式执行此操作的方法,但您可以自行尝试以下操作:

[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); // 测试成功(不正常)
}

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} 字段具有相同的值");
    }
}

但请注意,您需要谨慎,有许多因素需要考虑(包括属性而不仅仅是字段、私有/受保护字段和属性、一个对象中缺少的成员等)。

英文:

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

答案2

得分: 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

huangapple
  • 本文由 发表于 2023年7月13日 00:02:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76672487.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定