当前位置:  开发笔记 > 编程语言 > 正文

比较NUnit中两个对象之间的相等性

如何解决《比较NUnit中两个对象之间的相等性》经验,为你挑选了8个好方法。

我试图断言一个对象与另一个对象"相等".

对象只是具有一堆公共属性的类的实例.是否有一种简单的方法让NUnit基于属性断言相等?

这是我目前的解决方案,但我认为可能有更好的方法:

Assert.AreEqual(LeftObject.Property1, RightObject.Property1)
Assert.AreEqual(LeftObject.Property2, RightObject.Property2)
Assert.AreEqual(LeftObject.Property3, RightObject.Property3)
...
Assert.AreEqual(LeftObject.PropertyN, RightObject.PropertyN)

我想要的是与CollectionEquivalentConstraint一样的精神,其中NUnit验证两个集合的内容是否相同.



1> Juanma..:

如果由于任何原因无法覆盖Equals,则可以构建一个辅助方法,通过反射迭代公共属性并断言每个属性.像这样的东西:

public static class AssertEx
{
    public static void PropertyValuesAreEquals(object actual, object expected)
    {
        PropertyInfo[] properties = expected.GetType().GetProperties();
        foreach (PropertyInfo property in properties)
        {
            object expectedValue = property.GetValue(expected, null);
            object actualValue = property.GetValue(actual, null);

            if (actualValue is IList)
                AssertListsAreEquals(property, (IList)actualValue, (IList)expectedValue);
            else if (!Equals(expectedValue, actualValue))
                Assert.Fail("Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue);
        }
    }

    private static void AssertListsAreEquals(PropertyInfo property, IList actualList, IList expectedList)
    {
        if (actualList.Count != expectedList.Count)
            Assert.Fail("Property {0}.{1} does not match. Expected IList containing {2} elements but was IList containing {3} elements", property.PropertyType.Name, property.Name, expectedList.Count, actualList.Count);

        for (int i = 0; i < actualList.Count; i++)
            if (!Equals(actualList[i], expectedList[i]))
                Assert.Fail("Property {0}.{1} does not match. Expected IList with element {1} equals to {2} but was IList with element {1} equals to {3}", property.PropertyType.Name, property.Name, expectedList[i], actualList[i]);
    }
}


谢谢.然而,我不得不改变实际和预期的参数的顺序,因为预期是预期在实际之前是一个参数.
如果您的类型仅具有基本类型作为属性,则此方法很有用.但是,如果您的类型具有包含自定义类型的属性(不实现Equals),则它将失败.

2> Max..:

不要仅为了测试目的而重写Equals.这很乏味并影响域逻辑.代替,

使用JSON比较对象的数据

您的对象没有其他逻辑.没有额外的测试任务.

只需使用这个简单的方法:

public static void AreEqualByJson(object expected, object actual)
{
    var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
    var expectedJson = serializer.Serialize(expected);
    var actualJson = serializer.Serialize(actual);
    Assert.AreEqual(expectedJson, actualJson);
}

它看起来很棒.测试运行结果信息将显示包含的JSON字符串比较(对象图),因此您可以直接看到错误.

还要注意!如果您有更大的复杂对象并且只想比较它们的一部分,您可以(使用LINQ作为序列数据)创建匿名对象以与上述方法一起使用.

public void SomeTest()
{
    var expect = new { PropA = 12, PropB = 14 };
    var sut = loc.Resolve();
    var bigObjectResult = sut.Execute(); // This will return a big object with loads of properties 
    AssExt.AreEqualByJson(expect, new { bigObjectResult.PropA, bigObjectResult.PropB });
}


这是一个好主意.我会使用更新的Json.NET:var expectedJson = Newtonsoft.Json.JsonConvert.SerializeObject(expected);
这不适用于循环引用.使用https://github.com/kbilsted/StatePrinter/代替改进JSON方法的体验
这是真的@KokaChernov,有时你想要失败测试,​​如果不是排序相同,但如果你不想失败,如果排序不相同,你可以在列表上进行显式排序(使用linq),然后将它们传递给AreEqualByJson方法.在测试之前"重新安排"对象的简单变体是答案中的最后一个代码示例.所以我觉得这非常"普遍"!:)

3> dkl..:

试试FluentAssertions库:

dto.ShouldHave(). AllProperties().EqualTo(customer);

http://www.fluentassertions.com/

它也可以使用NuGet进行安装.


应该已经弃用了,所以应该是dto.ShouldBeEquivalentTo(客户); 代替
只是有同样的问题,并使用以下似乎工作正常:`actual.ShouldBeEquivalentTo(预期,x => x.ExcludingMissingMembers())`
这是[这个原因]的最佳答案(http://stackoverflow.com/a/28545338/62600).

4> angry person..:

覆盖对象的.Equals,然后在单元测试中,您可以简单地执行此操作:

Assert.AreEqual(LeftObject, RightObject);

当然,这可能意味着您只需将所有单独的比较移动到.Equals方法,但它允许您将该实现重用于多个测试,并且可能有必要让对象应该能够与兄弟姐妹进行比较.


更多警告:如果您将该对象用作键,那么在可变类型上实现`GetHashCode()`将会出错.恕我直言,重写`Equals()`,`GetHashCode()`并使对象不可变只是为了测试没有意义.
和GetHashCode(),显然;-p
那么你编写代码来测试你的测试代码?
谢谢,lassevk.这对我有用!我根据这里的指南实现了.Equals:http://msdn.microsoft.com/en-us/library/336aedhh(VS.80).aspx

5> 小智..:

我不想仅仅为了启用测试而重写Equals.不要忘记,如果你确实覆盖了Equals,你也应该覆盖GetHashCode,或者如果你在字典中使用你的对象,你可能会得到意想不到的结果.

我喜欢上面的反思方法,因为它适合将来添加属性.

对于快速而简单的解决方案,通常最简单的方法是创建一个帮助方法来测试对象是否相等,或者在一个类上实现IEqualityComparer,使其保持对测试的私密性.使用IEqualityComparer解决方案时,您不需要为GetHashCode的实现而烦恼.例如:

// Sample class.  This would be in your main assembly.
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// Unit tests
[TestFixture]
public class PersonTests
{
    private class PersonComparer : IEqualityComparer
    {
        public bool Equals(Person x, Person y)
        {
            if (x == null && y == null)
            {
                return true;
            }

            if (x == null || y == null)
            {
                return false;
            }

            return (x.Name == y.Name) && (x.Age == y.Age);
        }

        public int GetHashCode(Person obj)
        {
            throw new NotImplementedException();
        }
    }

    [Test]
    public void Test_PersonComparer()
    {
        Person p1 = new Person { Name = "Tom", Age = 20 }; // Control data

        Person p2 = new Person { Name = "Tom", Age = 20 }; // Same as control
        Person p3 = new Person { Name = "Tom", Age = 30 }; // Different age
        Person p4 = new Person { Name = "Bob", Age = 20 }; // Different name.

        Assert.IsTrue(new PersonComparer().Equals(p1, p2), "People have same values");
        Assert.IsFalse(new PersonComparer().Equals(p1, p3), "People have different ages.");
        Assert.IsFalse(new PersonComparer().Equals(p1, p4), "People have different names.");
    }
}



6> Todd Menier..:

我试过这里提到的几种方法.大多数涉及序列化对象和进行字符串比较.虽然超级简单且通常非常有效,但我发现当你出现故障并且有类似的事情被报告时,它会变得有点短:

Expected string length 2326 but was 2342. Strings differ at index 1729.

弄清楚差异所在的位置至少可以说是痛苦.

通过FluentAssertions的对象图比较(即a.ShouldBeEquivalentTo(b)),您可以得到这个:

Expected property Name to be "Foo" but found "Bar"

那更好.现在获得FluentAssertions,你会很高兴以后(如果你赞成这一点,请同时提出dkl 的回答,首先建议使用FluentAssertions).



7> Sly Gryphon..:

我同意ChrisYoxall - 在主要代码中实现Equals仅仅是为了测试目的并不好.

如果你正在实现Equals,因为某些应用程序逻辑需要它,那么这很好,但保持纯粹的仅测试代码的混乱(也是检查相同的测试语义可能与你的应用程序需要的不同).

简而言之,保持您的课程中仅测试代码.

对于大多数类,使用反射的属性的简单浅层比较应该足够了,尽管如果对象具有复杂属性,则可能需要递归.如果参考,请注意循环引用或类似内容.

狡猾



8> Paul Hicks..:

NUnit 2.4.2中添加的属性约束使解决方案比OP的原始解决方案更具可读性,并且产生的故障消息更好。它绝不是通用的,但是如果您不需要太多的类,那么这是一个非常合适的解决方案。

Assert.That(ActualObject, Has.Property("Prop1").EqualTo(ExpectedObject.Prop1)
                          & Has.Property("Prop2").EqualTo(ExpectedObject.Prop2)
                          & Has.Property("Prop3").EqualTo(ExpectedObject.Prop3)
                          // ...

Equals虽然不像实现那样具有通用性,但是与失败相比,它提供了更好的故障信息

Assert.AreEqual(ExpectedObject, ActualObject);

推荐阅读
手机用户2502852037
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有