XUnit Assertion用于检查对象的相等性

inq*_*ive 24 c# xunit xunit.net

我正在使用XUnit框架来测试我的C#代码.

这个框架中是否有可用于对象比较的断言方法?我的目的是检查每个对象的公共和私有成员变量是否相等.

我试过那些替代品但很少有效:

1) bool IsEqual = (Obj1 == Obj2)
2) Assert.Same(Obj1, Obj2) which I couldnt understand what happens internally
Run Code Online (Sandbox Code Playgroud)

小智 38

我有类似的问题,但幸运的是我已经在使用了

using Newtonsoft.Json;
Run Code Online (Sandbox Code Playgroud)

所以我只需要将它序列化为json对象然后比较为字符串.

var obj1Str = JsonConvert.SerializeObject(obj1);
var obj2Str = JsonConvert.SerializeObject(obj2);
Assert.Equal(obj1Str, obj2Str );
Run Code Online (Sandbox Code Playgroud)

  • 不保证这些对象将以相同的方式序列化,这里存在一些风险。 (3认同)
  • 我认为这比实现Equals方法更有用,因为我希望断言无法告诉我一些错误。我理想的情况是拥有可以遍历对象树并累积有关哪些属性/子树不相等并失败的信息。 (2认同)
  • @RikkiGibson 实施 Equals 不是正确的方法吗?只需将异常添加到每个不同属性的 equals 列表中,然后最后返回 true 或抛出所有异常 (2认同)

Ada*_*m T 37

从 2022 年 8 月 1 日发布的 Xunit 2.4.2 开始,此功能作为 Assert.Equivalent 实现。

例子:

Assert.Equivalent(Obj1, Obj2);
Run Code Online (Sandbox Code Playgroud)

或者

Assert.Equivalent(Obj1, Obj2, strict:true)
Run Code Online (Sandbox Code Playgroud)

请参阅发行说明:https://xunit.net/releases/v2/2.4.2


小智 18

FluentAssertions库内部有一些非常强大的比较逻辑。

myObject.ShouldBeEquivalentTo(new { SomeProperty = "abc", SomeOtherProperty = 23 });
Run Code Online (Sandbox Code Playgroud)

您甚至可以使用它来断言“myObject”的一部分。但是,它可能无法帮助您处理私有字段。

  • 我从岩石下爬出来,面对我必须面对的 xUnit 测试怪物……我说,这毫无意义。我内心的编码孩子总是必须被提醒,单元测试发脾气完全不适合这个年龄。嗯,那是在我发现 FluentAssertions 之前。那天就是今天。而你,@AdrianPetrescu,对这位睡眼惺忪的开发者表示由衷的赞赏。 (9认同)
  • 该库的当前版本似乎使用以下 API:“actual.Should().BeEquivalentTo(expected);” (8认同)

Baz*_*nga 13

您需要有一个自定义比较器来实现这一点,当您比较对象时,否则将根据它们是否引用内存中的同一对象来检查它们.要覆盖此行为,您需要覆盖EqualsGetHashCode方法,然后您可以执行以下操作:

Assert.True(obj1.Equals(obj2));
Run Code Online (Sandbox Code Playgroud)

这是一个MSDN页面abt重载Equals方法:http://msdn.microsoft.com/en-us/library/ms173147( v = vs.80).aspx

同样适用于这个问题的评论:IEquatable与重写Object.Equals()之间的区别是什么?

  • 据我所知,通过实现自定义"Equals"方法,可以执行此检查.但有没有任何方法可以进行盲字节比较,这将使检查更容易?这是因为我最终会在"被测软件"中进行"等于"实现,仅用于单元测试. (4认同)
  • 我尝试使用序列化方法将两个对象转换为字节数组并且它有效.但它带有一个约束,即将[serializable]属性添加到具有私有成员变量的类中.我想这在设计方面并不好 (2认同)
  • 我反对仅针对单元测试覆盖这两种方法。 (2认同)

mar*_*ice 7

有NuGet软件包可以为您执行此操作。这是我个人使用的两个示例。

  1. DeepEqual

    object1.ShouldDeepEqual(object2);
    
    Run Code Online (Sandbox Code Playgroud)
  2. ExpectedObjects

    [Fact]
    public void RetrievingACustomer_ShouldReturnTheExpectedCustomer()
    {
      // Arrange
      var expectedCustomer = new Customer
      {
        FirstName = "Silence",
        LastName = "Dogood",
        Address = new Address
        {
          AddressLineOne = "The New-England Courant",
          AddressLineTwo = "3 Queen Street",
          City = "Boston",
          State = "MA",
          PostalCode = "02114"
        }                                            
      }.ToExpectedObject();
    
    
      // Act
      var actualCustomer = new CustomerService().GetCustomerByName("Silence", "Dogood");
    
      // Assert
      expectedCustomer.ShouldEqual(actualCustomer);
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • @WillP。DeepEqual还没有官方的.NET Standard / Core支持。它可以工作,但可能会引起问题。否则,它们几乎是相同的。ExpectedObjects还有更多功能,例如部分比较或自定义比较。这就是为什么我现在几乎在我的所有项目中都使用ExpectedObjects的原因,但它更多是个人喜好。 (2认同)

Eng*_*rew 6

我知道这是一个古老的问题,但是由于偶然发现了这个问题,所以我想考虑使用一个可用的新解决方案(至少在.net Core 2.0解决方案的xunit 2.3.1中)。

我不确定何时引入它,但是现在有一个重载形式.Equal可以接受实例IEqualityComparer<T>作为第三个参数。您可以在单元测试中创建自定义比较器,而不会污染您的代码。

可以像这样调用以下代码: Assert.Equal(expectedParameters, parameters, new CustomComparer<ParameterValue>());

XUnit本身似乎在遇到故障后立即停止处理测试,因此EqualException从我们的比较器中抛出一个新值似乎与XUnit开箱即用的方式保持一致。

public class CustomComparer<T> : IEqualityComparer<T>
{
    public bool Equals(T expected, T actual)
    {
        var props = typeof(T).GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
        foreach (var prop in props)
        {
            var expectedValue = prop.GetValue(expected, null);
            var actualValue = prop.GetValue(actual, null);
            if (!expectedValue.Equals(actualValue))
            {
                throw new EqualException($"A value of \"{expectedValue}\" for property \"{prop.Name}\"",
                    $"A value of \"{actualValue}\" for property \"{prop.Name}\"");
            }
        }

        return true;
    }

    public int GetHashCode(T parameterValue)
    {
        return Tuple.Create(parameterValue).GetHashCode();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:我发现比较实际值和期望值!=对于某些类型是无效的(我确定有更好的解释涉及引用类型和值类型之间的区别,但是今天还不行)。我更新了代码以使用该.Equals方法比较两个值,这似乎更好。

  • 这行得通,但我宁愿只使用执行类似操作的NuGet包。减少我需要担心的代码 (2认同)
  • 你不应该在“等于”上“扔”。它可能会搞砸“Assert.DoesNotContain”,因为它预计每个条目都会得到 false。你最好返回“false”。 (2认同)