比较 NUnit 中两个对象之间的相等性
我试图断言一个对象“等于”另一个对象。
这些对象只是具有一堆公共属性的类的实例。 有没有一种简单的方法可以让 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 验证两个集合的内容是否相同。
I'm trying to assert that one object is "equal" to another object.
The objects are just instances of a class with a bunch of public properties. Is there an easy way to have NUnit assert equality based on the properties?
This is my current solution but I think there may be something better:
Assert.AreEqual(LeftObject.Property1, RightObject.Property1)
Assert.AreEqual(LeftObject.Property2, RightObject.Property2)
Assert.AreEqual(LeftObject.Property3, RightObject.Property3)
...
Assert.AreEqual(LeftObject.PropertyN, RightObject.PropertyN)
What I'm going for would be in the same spirit as the CollectionEquivalentConstraint wherein NUnit verifies that the contents of two collections are identical.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(20)
不要仅出于测试目的而覆盖 Equals。 这很乏味并且影响领域逻辑。
相反,
使用 JSON 来比较对象的数据
对象上没有额外的逻辑。 没有额外的测试任务。
只需使用这个简单的方法:
看起来效果很好。 测试运行程序结果信息将显示包含的 JSON 字符串比较(对象图),以便您直接看到问题所在。
另请注意!如果您有更大的复杂对象并且只想比较其中的一部分,您可以(使用 LINQ 获取序列数据)创建匿名对象以与上述方法一起使用。
Do not override Equals just for testing purposes. It's tedious and affects domain logic.
Instead,
Use JSON to compare the object's data
No additional logic on your objects. No extra tasks for testing.
Just use this simple method:
It seems to work out great. The test runner results info will show the JSON string comparison (the object graph) included so you see directly what's wrong.
Also note! If you have bigger complex objects and just want to compare parts of them you can (use LINQ for sequence data) create anonymous objects to use with above method.
尝试 FluentAssertions 库:
它也可以使用 NuGet 安装。
Try FluentAssertions library:
It can also be installed using NuGet.
如果出于任何原因无法重写 Equals,则可以构建一个帮助器方法,通过反射迭代公共属性并断言每个属性。 像这样的东西:
If you can't override Equals for any reason, you can build a helper method that iterates through public properties by reflection and assert each property. Something like this:
为您的对象覆盖 .Equals ,然后在单元测试中您可以简单地执行以下操作:
当然,这可能意味着您只需将所有单独的比较移至 .Equals 方法,但它允许您在多个测试中重用该实现,如果对象应该能够将自己与兄弟姐妹进行比较,那么这可能是有意义的。
Override .Equals for your object and in the unit test you can then simply do this:
Of course, this might mean you just move all the individual comparisons to the .Equals method, but it would allow you to reuse that implementation for multiple tests, and probably makes sense to have if objects should be able to compare themselves with siblings anyway.
我不想仅仅为了启用测试而重写 Equals。 不要忘记,如果您确实重写了 Equals,那么您确实应该也重写 GetHashCode,否则如果您在字典中使用对象,则可能会得到意外的结果。
我确实喜欢上面的反射方法,因为它适合将来添加属性。
然而,对于快速简单的解决方案,通常最简单的方法是创建一个测试对象是否相等的辅助方法,或者在您对测试保持私有的类上实现 IEqualityComparer。 使用 IEqualityComparer 解决方案时,您无需担心 GetHashCode 的实现。 例如:
I prefer not to override Equals just to enable testing. Don't forget that if you do override Equals you really should override GetHashCode also or you may get unexpected results if you are using your objects in a dictionary for example.
I do like the reflection approach above as it caters for the addition of properties in the future.
For a quick and simple solution however its often easiest to either create a helper method that tests if the objects are equal, or implement IEqualityComparer on a class you keep private to your tests. When using IEqualityComparer solution you dont need to bother with the implementation of GetHashCode. For example:
我尝试了这里提到的几种方法。 大多数涉及序列化对象并进行字符串比较。 虽然超级简单并且通常非常有效,但我发现当您遇到失败并且报告类似这样的事情时,它会有点短:
至少可以说,找出差异在哪里是一件痛苦的事情。
使用 FluentAssertions 的对象图比较(即
a.ShouldBeEquivalentTo( b)
),你会得到这样的结果:那好多了。 立即获取 FluentAssertions,稍后您会很高兴(如果您对此表示赞同,请也投票 < a href="https://stackoverflow.com/a/7440471/62600">dkl 的答案,其中首次建议使用 FluentAssertions)。
I've tried several approaches mentioned here. Most involve serializing your objects and doing a string compare. While super easy and generally very effective, I've found it comes up a little short when you have a failure and something like this gets reported:
Figuring out where where the differences are is a pain to say the least.
With FluentAssertions' object graph comparisons (i.e.
a.ShouldBeEquivalentTo(b)
), you get this back:That's much nicer. Get FluentAssertions now, you'll be glad later (and if you upvote this, please also upvote dkl's answer where FluentAssertions was first suggested).
我同意 ChrisYoxall 的观点——纯粹出于测试目的在主代码中实现 Equals 是不好的。
如果您实现 Equals 是因为某些应用程序逻辑需要它,那么这很好,但请保留纯测试代码以免混乱(而且检查相同测试的语义可能与您的应用程序所需的不同)。
简而言之,将仅用于测试的代码保留在类之外。
使用反射对属性进行简单的浅层比较对于大多数类来说应该足够了,尽管如果您的对象具有复杂的属性,您可能需要递归。 如果遵循引用,请注意循环引用或类似引用。
狡猾
I agree with ChrisYoxall -- implementing Equals in your main code purely for testing purposes is not good.
If you are implementing Equals because some application logic requires it, then that's fine, but keep pure testing-only code out of cluttering up stuff (also the semantics of checking the same for testing may be different than what your app requires).
In short, keep testing-only code out of your class.
Simple shallow comparison of properties using reflection should be enough for most classes, although you may need to recurse if your objects have complex properties. If following references, beware of circular references or similar.
Sly
属性约束,在 NUnit 2.4.2 中添加,允许这是一种比 OP 的原始解决方案更具可读性的解决方案,并且它会产生更好的失败消息。 它无论如何都不是通用的,但如果您不需要为太多类执行此操作,那么它是一个非常合适的解决方案。
不像实现 Equals 那样通用,但它确实提供了比实现更好的失败消息
Property constraints, added in NUnit 2.4.2, allow a solution that is more readable than the OP's original one, and it produces much better failure messages. It's not in any way generic, but if you don't need to do it for too many classes, it's a very adequate solution.
Not as general-purpose as implementing
Equals
but it does give a much better failure message thanMax Wikstrom 的 JSON 解决方案(上图)对我来说最有意义,它简短、干净,最重要的是它有效。 就我个人而言,虽然我更喜欢将 JSON 转换作为单独的方法来实现,并将断言放回单元测试中,如下所示...
帮助方法:
单元测试:
仅供参考 - 您可能需要添加对 System.Web.Extensions 的引用在你的解决方案中。
Max Wikstrom's JSON solution (above) makes the most sense to me, it's short, clean and most importantly it works. Personally though I'd prefer to implement the JSON conversion as a separate method and place the assert back inside the unit test like this...
HELPER METHOD:
UNIT TEST :
FYI - You may need to add a reference to System.Web.Extensions in your solution.
只需安装 Nuget 的 ExpectedObjects,您就可以轻松比较两个对象的属性值、集合的每个对象值、两个组合对象的值以及匿名类型的部分比较属性值。
我在 github 上有一些示例: https://github.com/hatelove/CompareObjectEquals
以下是一些示例包含比较对象的场景:
参考:
Just install ExpectedObjects from Nuget, you can easily compare two objects's property value, each object value of collection, two composed object's value and partial compare property value by anonymous type.
I have some examples on github: https://github.com/hatelove/CompareObjectEquals
Here were some examples that contain scenarios of comparing object:
Reference:
这是一个非常古老的线程,但我想知道是否有原因没有提出答案
NUnit.Framework.Is.EqualTo
和NUnit.Framework.Is.NotEqualTo
?如:
和
This is a pretty old thread but I was wondering if there's a reason why no answer proposed
NUnit.Framework.Is.EqualTo
andNUnit.Framework.Is.NotEqualTo
?Such as:
and
另一种选择是通过实现 NUnit 抽象 Constraint 类来编写自定义约束。 使用帮助程序类提供一点语法糖,生成的测试代码非常简洁且可读,例如,
举一个极端的例子,考虑具有“只读”成员的类,它不是
IEquatable
,并且您即使您愿意,也无法更改被测类:Constraint
类的约定要求重写Matches
和WriteDescriptionTo
(在不匹配的情况下,预期值的叙述),但也重写WriteActualValueTo
(实际值的叙述)是有意义的:加上辅助类:
示例用法:
Another option is to write a custom constraint by implementing the NUnit abstract
Constraint
class. With a helper class to provide a little syntactic sugar, the resulting test code is pleasantly terse and readable e.g.For an extreme example, consider class which has 'read-only' members, is not
IEquatable
, and you could not change the class under test even if you wanted to:The contract for the
Constraint
class requires one to overrideMatches
andWriteDescriptionTo
(in the case of a mismatch, a narrative for the expected value) but also overridingWriteActualValueTo
(narrative for actual value) makes sense:Plus the helper class:
Example usage:
我会以@Juanma 的答案为基础。 但是,我认为这不应该通过单元测试断言来实现。 这是一个在某些情况下很可能被非测试代码使用的实用程序。
我写了一篇关于此事的文章 http:// /timoch.com/blog/2013/06/unit-test-equality-is-not-domain-equality/
我的建议如下:
将其与 NUnit 一起使用
会在不匹配时产生以下消息。
I would build on the answer of @Juanma. However, I believe this should not be implemented with unit test assertions. This is a utility that could very well be used in some circumstances by non-test code.
I wrote an article on the matter http://timoch.com/blog/2013/06/unit-test-equality-is-not-domain-equality/
My proposal is as follow:
Using this with NUnit
yields the following message on mismatch.
https://github.com/kbilsted/StatePrinter 专门用于将对象图转储为字符串表示形式编写简单的单元测试的目的。
给定
您可以以类型安全的方式,并使用自动完成Visual Studio 的包含或排除字段。
https://github.com/kbilsted/StatePrinter has been written specifically to dump object graphs to string representation with the aim of writing easy unit tests.
Given
You can in a type safe manner, and using auto-completion of visual studio include or exclude fields.
我最终编写了一个简单的表达式工厂:
然后使用它:
它非常有用,因为我必须比较此类对象的集合。 您可以在其他地方使用此比较:)
以下是示例要点: https://gist.github。 com/Pzixel/b63fea074864892f9aba8ffde312094f
I've ended with writing a simple expression factory:
and just use it:
It's very useful since I have to compare collection of such objects. And you can use this comparere somewhere else :)
Here is gist with example: https://gist.github.com/Pzixel/b63fea074864892f9aba8ffde312094f
反序列化这两个类,并进行字符串比较。
编辑:
工作完美,这是我从 NUnit 得到的输出;
编辑二:
两个对象可以相同,但属性的序列化顺序不同。 因此 XML 是不同的。 卫生部!
编辑三:
这确实有效。 我在测试中使用它。 但是您必须按照测试代码添加项目的顺序将项目添加到集合属性中。
Deserialize both classes, and do a string compare.
EDIT:
Works perfectly, this is the output I get from NUnit;
EDIT TWO:
The two objects can be identical, but the order that properties are serialized in are not the same. Therefore the XML is different. DOH!
EDIT THREE:
This does work. I am using it in my tests. But you must add items to collection properties in the order the code under test adds them.
我知道这是一个非常老的问题,但 NUnit 仍然没有对此提供本机支持。 然而,如果您喜欢 BDD 风格的测试(ala Jasmine),您会对 NExpect 感到惊喜(https:/ /github.com/fluffynuts/NExpect,从 NuGet 获取),其中包含深度平等测试。
(免责声明:我是 NExpect 的作者)
I know this is a really old question, but NUnit still doesn't have native support for this. However, if you like BDD-style testing (ala Jasmine), you'd be pleasantly surprised with NExpect (https://github.com/fluffynuts/NExpect, get it from NuGet), which has deep equality testing baked right in there.
(disclaimer: I am the author of NExpect)
这只是上述答案之一的修改版本,可与 Moq 一起使用:
当您需要的内容不只是
It.IsAny<> 时,它可用于在模拟类型上指定设置时匹配对象。
并希望匹配所有属性,如下所示:当然可以改进它以处理可枚举和其他复杂场景。
Here is just a modified version of one of the answers above that can be used with Moq:
It can be used to match an object when specifying a setup on a mocked type when you need something more than
It.IsAny<>
and want to match on all properties, like this:It can of course be improved to work with enumerables and other complex scenarios.
Compare-Net-Objects 项目已内置于 测试扩展 支持比较 NUnit 中的嵌套对象。
The Compare-Net-Objects project has built in test extensions to support comparing nested objects within NUnit.
对两个字符串进行字符串化和比较
Assert.AreEqual(JSON.stringify(LeftObject), JSON.stringify(RightObject))
Stringify and compare two strings
Assert.AreEqual(JSON.stringify(LeftObject), JSON.stringify(RightObject))