NUnit에서 두 객체 간의 동등성을 비교하십시오.


126

한 객체가 다른 객체와 "동일"하다고 주장하려고합니다.

객체는 공용 속성이 많은 클래스의 인스턴스 일뿐입니다. 속성을 기반으로 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)

내가 가고 싶은 것은 NUnit이 두 컬렉션의 내용이 동일한 지 확인하는 CollectionEquivalentConstraint와 같은 정신에 있습니다.

답변:


51

재정의. 객체와 동일하고 단위 테스트에서 간단히 다음을 수행 할 수 있습니다.

Assert.AreEqual(LeftObject, RightObject);

물론 이것은 모든 개별 비교를 .Equals 메소드로 옮기는 것을 의미 할 수 있지만 여러 테스트에 해당 구현을 재사용 할 수 있으며 객체가 형제와 자신을 비교할 수 있어야하는 경우가 있습니다.


2
고마워, lassevk. 이것은 나를 위해 일했다! : 여기 지침에 따라 .Equals을 구현 msdn.microsoft.com/en-us/library/336aedhh(VS.80).aspx
마이클 하렌

12
그리고 GetHashCode (), 분명히 ;-p
Marc Gravell

해당 페이지의 목록에서 1 번은 GetHashCode를 재정의하는 것이며, 그는 다음 지침을 준수한다고 말했습니다.) 그렇습니다. 일반적으로 실수는 아니지만 대부분의 경우에 눈치 채지 않을 것입니다. 그러나 그렇게 할 때, "오, 왜이게 내 바지에 뱀이 있고 왜 내 엉덩이를 물고 있습니까?"라고 말할 때와 비슷합니다.
Lasse V. Karlsen

1
한 가지 중요한주의 사항 : 객체도 구현 IEnumerable하는 경우 EqualsNUnit이 IEnumerable우선 순위 를 높이기 때문에 구현을 재정의하지 않고 컬렉션으로 비교됩니다 . 자세한 내용은 NUnitEqualityComparer.AreEqual방법을 참조하십시오. 등식 제약 조건의 Using()방법 중 하나를 사용하여 비교자를 재정의 할 수 있습니다 . 그럼에도 불구 IEqualityComparer하고 NUnit이 사용하는 어댑터 때문에 비 제네릭을 구현하는 것만으로는 충분하지 않습니다 .
Kaleb Pederson

13
주의 사항 : GetHashCode()변경 가능한 유형을 구현 하면 해당 객체를 키로 사용하는 경우 제대로 작동하지 않습니다. IMHO, 재정 Equals()GetHashCode()및 테스트를 위해 객체를 변경할 수 없게 만드는 것은 의미가 없습니다.
bavaza

118

어떤 이유로 든 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]);
    }
}

@ 웨스 : 이것은 사실이 아닙니다. Type.GetProperties 메서드 : 현재 Type의 모든 공용 속성을 반환합니다. msdn.microsoft.com/en-us/library/aky14axb.aspx
Sergii Volchkov 2018

4
감사. 그러나 예상되는 것이 실제 이전의 매개 변수이기 때문에 집행 및 실제 매개 변수의 순서를 전환해야했습니다.
Valamas

이것은 IMHO, Equal & HashCode 재정의가 모든 필드와 모든 객체를 비교하는 데 매우 지루한 비교를 기반으로 할 필요는 없습니다. 잘 했어!
Scott White

3
유형에 속성으로 기본 유형만있는 경우 효과적입니다. 그러나 유형에 사용자 정의 유형 (동일을 구현하지 않는)이있는 속성이 있으면 실패합니다.
Bobby Cannon

객체 속성에 대한 재귀를 추가했지만 인덱스 속성을 건너 뛰어야했습니다.
cerhart

113

테스트 목적으로 만 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<SomeSvc>();
    var bigObjectResult = sut.Execute(); // This will return a big object with loads of properties 
    AssExt.AreEqualByJson(expect, new { bigObjectResult.PropA, bigObjectResult.PropB });
}

1
이는 특히 JSON을 다루는 경우 (예 : 유형이 지정된 클라이언트를 사용하여 웹 서비스에 액세스하는 경우) 테스트하는 훌륭한 방법입니다. 이 답변은 훨씬 높아야합니다.
Roopesh Shenoy

1
Linq를 사용하십시오! @DmitryBLR (답변의 마지막 단락 참조) :)
Max

3
이것은 좋은 생각입니다. 최신 Json.NET을 사용하겠습니다. var expectJson = Newtonsoft.Json.JsonConvert.SerializeObject (expected);
BrokeMyLegBiking

2
순환 참조에서는 작동하지 않습니다. JSON 접근 방식에 대한 향상된 경험을 위해 대신 github.com/kbilsted/StatePrinter 를 사용하십시오
Carlo V. Dango

2
사실 @KokaChernov이며 주문이 동일하지 않은 경우 테스트에 실패하고 싶지만 주문이 동일하지 않은 경우 실패하지 않으려는 경우 AreEqualByJson 메서드에 전달하기 전에 목록에서 명시 적 정렬 (linq 사용)을 만들 수 있습니다. 테스트하기 전에 객체를 "재정렬"하는 간단한 변형은 마지막 코드 예입니다. 그래서 그것은 매우 "보편적"이라고 생각합니다! :)
최대

91

FluentAssertions 라이브러리를 사용해보십시오.

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

http://www.fluentassertions.com/

NuGet을 사용하여 설치할 수도 있습니다.


18
ShouldHave는 더 이상 사용되지 않으므로 dto.ShouldBeEquivalentTo (customer); 대신
WhiteKnight

2
이것이 바로이 이유에 대한 가장 좋은 대답입니다 .
Todd Menier

shouldBeEquivalent는 버그입니다 :(
Konstantin Chernov

3
방금 같은 문제가 있었고 다음과 같이 잘 작동하는 것 같습니다.actual.ShouldBeEquivalentTo(expected, x => x.ExcludingMissingMembers())
stt106

1
이것은 훌륭한 라이브러리입니다! Equals를 재정의 할 필요가 없으며 값 개체에 대해 equals가 재정의되는 경우에도 올바른 구현에 의존하지 않습니다. 또한 Hamcrest가 Java에 대해하는 것처럼 차이점이 멋지게 인쇄됩니다.
kap

35

테스트를 활성화하기 위해 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<Person>
    {
        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.");
    }
}

equals는 null 값을 처리하지 않습니다. equals 메소드에서 return 문 앞에 다음을 추가합니다. if (x == null && y == null) {return true; } if (x == null || y == null) {return false; } null 지원을 추가하기 위해 질문을 편집했습니다.
Bobby Cannon

throw new NotImplementedException ()으로 나를 위해 일하지 않습니다. GetHashCode에서. IEqualityComparer에서 해당 기능이 필요한 이유는 무엇입니까?
love2code

15

여기에 언급 된 여러 가지 접근법을 시도했습니다. 대부분 객체를 직렬화하고 문자열 비교를 수행합니다. 매우 쉽고 일반적으로 매우 효과적이지만, 실패했을 때 약간 짧아지고 다음과 같은 것이보고됩니다.

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를 지금 받으십시오. 나중에 기뻐할 것입니다 (이를 찬성한다면 FluentAssertions가 처음 제안 된 곳 의 dkl의 답변찬성 하십시오 ).


9

ChrisYoxall에 동의합니다. 테스트 목적으로 순수하게 주 코드에서 Equals를 구현하는 것은 좋지 않습니다.

일부 응용 프로그램 논리에 필요하기 때문에 Equals를 구현하는 경우 괜찮습니다. 그러나 테스트 전용 코드를 깔끔하게 유지하십시오 (또한 테스트를 위해 동일한 검사의 의미가 앱에 필요한 것과 다를 수 있음).

간단히 말해, 테스트 전용 코드는 수업에서 제외하십시오.

대부분의 클래스에는 리플렉션을 사용하여 속성을 간단하게 비교하면 충분하지만 개체에 복잡한 속성이있는 경우 재귀가 필요할 수 있습니다. 다음 참조를 할 경우 순환 참조 또는 이와 유사한 것을주의하십시오.

교활한


순환 참조를 잘 잡습니다. 비교 트리에 이미 개체 사전을 유지하면 쉽게 극복 할 수 있습니다.
Lucas B

6

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

4

Max Wikstrom의 JSON 솔루션 (위)은 나에게 가장 합리적이며 짧고 깨끗하며 가장 중요합니다. 개인적으로 JSON 변환을 별도의 방법으로 구현하고 assert를 다음과 같이 단위 테스트 안에 다시 배치하는 것을 선호합니다 ...

도우미 방법 :

public string GetObjectAsJson(object obj)
    {
        System.Web.Script.Serialization.JavaScriptSerializer oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
        return oSerializer.Serialize(obj);
    }

단위 테스트 :

public void GetDimensionsFromImageTest()
        {
            Image Image = new Bitmap(10, 10);
            ImageHelpers_Accessor.ImageDimensions expected = new ImageHelpers_Accessor.ImageDimensions(10,10);

            ImageHelpers_Accessor.ImageDimensions actual;
            actual = ImageHelpers_Accessor.GetDimensionsFromImage(Image);

            /*USING IT HERE >>>*/
            Assert.AreEqual(GetObjectAsJson(expected), GetObjectAsJson(actual));
        }

참고-솔루션에 System.Web.Extensions에 대한 참조를 추가해야 할 수도 있습니다.


4

이것은 꽤 오래된 스레드하지만 대답은 제안하지 왜 이유가 궁금 해서요 NUnit.Framework.Is.EqualTo그리고 NUnit.Framework.Is.NotEqualTo?

같은 :

Assert.That(LeftObject, Is.EqualTo(RightObject)); 

Assert.That(LeftObject, Is.Not.EqualTo(RightObject)); 

4
다른 내용을 인쇄하지 않기 때문에
Shrage Smilowitz

1

또 다른 옵션은 NUnit 추상 Constraint클래스를 구현하여 사용자 정의 제한 조건을 작성하는 것 입니다. 약간의 구문 설탕을 제공하는 도우미 클래스를 사용하면 결과 테스트 코드를 즐겁게 읽을 수 있습니다.

Assert.That( LeftObject, PortfolioState.Matches( RightObject ) ); 

극단적 인 예를 들어, '읽기 전용'멤버를 가진 class is not IEquatable을 고려하여 다음과 같은 경우에도 테스트중인 클래스를 변경할 수 없습니다.

public class Portfolio // Somewhat daft class for pedagogic purposes...
{
    // Cannot be instanitated externally, instead has two 'factory' methods
    private Portfolio(){ }

    // Immutable properties
    public string Property1 { get; private set; }
    public string Property2 { get; private set; }  // Cannot be accessed externally
    public string Property3 { get; private set; }  // Cannot be accessed externally

    // 'Factory' method 1
    public static Portfolio GetPortfolio(string p1, string p2, string p3)
    {
        return new Portfolio() 
        { 
            Property1 = p1, 
            Property2 = p2, 
            Property3 = p3 
        };
    }

    // 'Factory' method 2
    public static Portfolio GetDefault()
    {
        return new Portfolio() 
        { 
            Property1 = "{{NONE}}", 
            Property2 = "{{NONE}}", 
            Property3 = "{{NONE}}" 
        };
    }
}

Constraint클래스 계약은 재정의해야 Matches하며 WriteDescriptionTo(일치하지 않는 경우 예상 값에 대한 설명) 재정의 WriteActualValueTo(실제 값에 대한 설명)가 의미가 있습니다.

public class PortfolioEqualityConstraint : Constraint
{
    Portfolio expected;
    string expectedMessage = "";
    string actualMessage = "";

    public PortfolioEqualityConstraint(Portfolio expected)
    {
        this.expected = expected;
    }

    public override bool Matches(object actual)
    {
        if ( actual == null && expected == null ) return true;
        if ( !(actual is Portfolio) )
        { 
            expectedMessage = "<Portfolio>";
            actualMessage = "null";
            return false;
        }
        return Matches((Portfolio)actual);
    }

    private bool Matches(Portfolio actual)
    {
        if ( expected == null && actual != null )
        {
            expectedMessage = "null";
            expectedMessage = "non-null";
            return false;
        }
        if ( ReferenceEquals(expected, actual) ) return true;

        if ( !( expected.Property1.Equals(actual.Property1)
                 && expected.Property2.Equals(actual.Property2) 
                 && expected.Property3.Equals(actual.Property3) ) )
        {
            expectedMessage = expected.ToStringForTest();
            actualMessage = actual.ToStringForTest();
            return false;
        }
        return true;
    }

    public override void WriteDescriptionTo(MessageWriter writer)
    {
        writer.WriteExpectedValue(expectedMessage);
    }
    public override void WriteActualValueTo(MessageWriter writer)
    {
        writer.WriteExpectedValue(actualMessage);
    }
}

또한 도우미 클래스 :

public static class PortfolioState
{
    public static PortfolioEqualityConstraint Matches(Portfolio expected)
    {
        return new PortfolioEqualityConstraint(expected);
    }

    public static string ToStringForTest(this Portfolio source)
    {
        return String.Format("Property1 = {0}, Property2 = {1}, Property3 = {2}.", 
            source.Property1, source.Property2, source.Property3 );
    }
}

사용법 예 :

[TestFixture]
class PortfolioTests
{
    [Test]
    public void TestPortfolioEquality()
    {
        Portfolio LeftObject 
            = Portfolio.GetDefault();
        Portfolio RightObject 
            = Portfolio.GetPortfolio("{{GNOME}}", "{{NONE}}", "{{NONE}}");

        Assert.That( LeftObject, PortfolioState.Matches( RightObject ) );
    }
}

1

@Juanma의 답변을 기반으로 작성합니다. 그러나 이것이 단위 테스트 어설 션으로 구현되어서는 안된다고 생각합니다. 이것은 테스트 환경이 아닌 코드에 의해 어떤 환경에서 사용될 수있는 유틸리티입니다.

나는 http://timoch.com/blog/2013/06/unit-test-equality-is-not-domain-equality/ 에 관한 기사를 썼습니다.

내 제안은 다음과 같습니다.

/// <summary>
/// Returns the names of the properties that are not equal on a and b.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns>An array of names of properties with distinct 
///          values or null if a and b are null or not of the same type
/// </returns>
public static string[] GetDistinctProperties(object a, object b) {
    if (object.ReferenceEquals(a, b))
        return null;
    if (a == null)
        return null;
    if (b == null)
        return null;

    var aType = a.GetType();
    var bType = b.GetType();

    if (aType != bType)
        return null;

    var props = aType.GetProperties();

    if (props.Any(prop => prop.GetIndexParameters().Length != 0))
        throw new ArgumentException("Types with index properties not supported");

    return props
        .Where(prop => !Equals(prop.GetValue(a, null), prop.GetValue(b, null)))
        .Select(prop => prop.Name).ToArray();
} 

이것을 NUnit과 함께 사용

Expect(ReflectionUtils.GetDistinctProperties(tile, got), Empty);

불일치에 대해 다음 메시지를 생성합니다.

Expected: <empty>
But was:  < "MagmaLevel" >
at NUnit.Framework.Assert.That(Object actual, IResolveConstraint expression, String message, Object[] args)
at Undermine.Engine.Tests.TileMaps.BasicTileMapTests.BasicOperations() in BasicTileMapTests.cs: line 29

1

https://github.com/kbilsted/StatePrinter 는 쉬운 단위 테스트를 작성하기 위해 객체 그래프를 문자열 표현으로 덤프하기 위해 특별히 작성되었습니다.

  • 올바르게 이스케이프 된 문자열 쉬운 복사-붙여 넣기를 테스트에 출력하여 수정하는 witg Assert 메소드가 제공됩니다.
  • 단위 테스트를 자동으로 다시 작성할 수 있습니다
  • 모든 단위 테스트 프레임 워크와 통합
  • JSON 직렬화와 달리 순환 참조가 지원됩니다.
  • 쉽게 필터링 할 수 있으므로 유형의 일부만 덤프됩니다.

주어진

class A
{
  public DateTime X;
  public DateTime Y { get; set; }
  public string Name;
}

안전한 형식으로 Visual Studio 포함 또는 제외 필드의 자동 완성 기능을 사용할 수 있습니다.

  var printer = new Stateprinter();
  printer.Configuration.Projectionharvester().Exclude<A>(x => x.X, x => x.Y);

  var sut = new A { X = DateTime.Now, Name = "Charly" };

  var expected = @"new A(){ Name = ""Charly""}";
  printer.Assert.PrintIsSame(expected, sut);

1

Nuget에서 ExpectedObjects를 설치하기 만하면 두 개체의 속성 값, 컬렉션의 각 개체 값, 두 개의 구성된 개체 값 및 부분 비교 속성 값을 익명 형식으로 쉽게 비교할 수 있습니다.

github에 대한 몇 가지 예가 있습니다. https://github.com/hatelove/CompareObjectEquals

다음은 객체 비교 시나리오를 포함하는 몇 가지 예입니다.

    [TestMethod]
    public void Test_Person_Equals_with_ExpectedObjects()
    {
        //use extension method ToExpectedObject() from using ExpectedObjects namespace to project Person to ExpectedObject
        var expected = new Person
        {
            Id = 1,
            Name = "A",
            Age = 10,
        }.ToExpectedObject();

        var actual = new Person
        {
            Id = 1,
            Name = "A",
            Age = 10,
        };

        //use ShouldEqual to compare expected and actual instance, if they are not equal, it will throw a System.Exception and its message includes what properties were not match our expectation.
        expected.ShouldEqual(actual);
    }

    [TestMethod]
    public void Test_PersonCollection_Equals_with_ExpectedObjects()
    {
        //collection just invoke extension method: ToExpectedObject() to project Collection<Person> to ExpectedObject too
        var expected = new List<Person>
        {
            new Person { Id=1, Name="A",Age=10},
            new Person { Id=2, Name="B",Age=20},
            new Person { Id=3, Name="C",Age=30},
        }.ToExpectedObject();

        var actual = new List<Person>
        {
            new Person { Id=1, Name="A",Age=10},
            new Person { Id=2, Name="B",Age=20},
            new Person { Id=3, Name="C",Age=30},
        };

        expected.ShouldEqual(actual);
    }

    [TestMethod]
    public void Test_ComposedPerson_Equals_with_ExpectedObjects()
    {
        //ExpectedObject will compare each value of property recursively, so composed type also simply compare equals.
        var expected = new Person
        {
            Id = 1,
            Name = "A",
            Age = 10,
            Order = new Order { Id = 91, Price = 910 },
        }.ToExpectedObject();

        var actual = new Person
        {
            Id = 1,
            Name = "A",
            Age = 10,
            Order = new Order { Id = 91, Price = 910 },
        };

        expected.ShouldEqual(actual);
    }

    [TestMethod]
    public void Test_PartialCompare_Person_Equals_with_ExpectedObjects()
    {
        //when partial comparing, you need to use anonymous type too. Because only anonymous type can dynamic define only a few properties should be assign.
        var expected = new
        {
            Id = 1,
            Age = 10,
            Order = new { Id = 91 }, // composed type should be used anonymous type too, only compare properties. If you trace ExpectedObjects's source code, you will find it invoke config.IgnoreType() first.
        }.ToExpectedObject();

        var actual = new Person
        {
            Id = 1,
            Name = "B",
            Age = 10,
            Order = new Order { Id = 91, Price = 910 },
        };

        // partial comparing use ShouldMatch(), rather than ShouldEqual()
        expected.ShouldMatch(actual);
    }

참고:

  1. 예상 객체 github
  2. 기대 대상 소개


1

나는 간단한 표현 팩토리를 작성하는 것으로 끝났습니다.

public static class AllFieldsEqualityComprision<T>
{
    public static Comparison<T> Instance { get; } = GetInstance();

    private static Comparison<T> GetInstance()
    {
        var type = typeof(T);
        ParameterExpression[] parameters =
        {
            Expression.Parameter(type, "x"),
            Expression.Parameter(type, "y")
        };
        var result = type.GetProperties().Aggregate<PropertyInfo, Expression>(
            Expression.Constant(true),
            (acc, prop) =>
                Expression.And(acc,
                    Expression.Equal(
                        Expression.Property(parameters[0], prop.Name),
                        Expression.Property(parameters[1], prop.Name))));
        var areEqualExpression = Expression.Condition(result, Expression.Constant(0), Expression.Constant(1));
        return Expression.Lambda<Comparison<T>>(areEqualExpression, parameters).Compile();
    }
}

그리고 그냥 사용하십시오 :

Assert.That(
    expectedCollection, 
    Is.EqualTo(actualCollection)
      .Using(AllFieldsEqualityComprision<BusinessCategoryResponse>.Instance));

그러한 객체의 컬렉션을 비교해야하기 때문에 매우 유용합니다. 그리고 당신은 다른 곳 에서이 비교를 사용할 수 있습니다 :)

여기에 예가 있습니다 : https://gist.github.com/Pzixel/b63fea074864892f9aba8ffde312094f


0

두 클래스를 역 직렬화하고 문자열 비교를 수행하십시오.

편집 : 완벽하게 작동합니다. 이것은 NUnit에서 얻은 결과입니다.

Test 'Telecom.SDP.SBO.App.Customer.Translator.UnitTests.TranslateEaiCustomerToDomain_Tests.TranslateNew_GivenEaiCustomer_ShouldTranslateToDomainCustomer_Test("ApprovedRatingInDb")' failed:
  Expected string length 2841 but was 5034. Strings differ at index 443.
  Expected: "...taClasses" />\r\n  <ContactMedia />\r\n  <Party i:nil="true" /..."
  But was:  "...taClasses" />\r\n  <ContactMedia>\r\n    <ContactMedium z:Id="..."
  ----------------------------------------------^
 TranslateEaiCustomerToDomain_Tests.cs(201,0): at Telecom.SDP.SBO.App.Customer.Translator.UnitTests.TranslateEaiCustomerToDomain_Tests.Assert_CustomersAreEqual(Customer expectedCustomer, Customer actualCustomer)
 TranslateEaiCustomerToDomain_Tests.cs(114,0): at Telecom.SDP.SBO.App.Customer.Translator.UnitTests.TranslateEaiCustomerToDomain_Tests.TranslateNew_GivenEaiCustomer_ShouldTranslateToDomainCustomer_Test(String custRatingScenario)

두 가지 편집 : 두 개체는 동일 할 수 있지만 속성이 직렬화되는 순서는 동일하지 않습니다. 따라서 XML이 다릅니다. 도!

세 번째 편집 : 작동합니다. 내 테스트에서 사용하고 있습니다. 그러나 테스트중인 코드가 추가하는 순서대로 컬렉션 속성에 항목을 추가해야합니다.


1
직렬화 ? 재미있는 생각. 그래도 성능 측면에서 어떻게 유지 될지 잘 모르겠습니다.
Michael Haren

주어진 정밀도와 두 배 또는 소수를 비교할 수 없습니다.
Noctis

0

나는 이것이 정말로 오래된 질문이라는 것을 알고 있지만 NUnit은 여전히 ​​이것을 기본적으로 지원하지 않습니다. 그러나 BDD 스타일 테스트 (ala Jasmine)를 좋아한다면 NExpect ( https://github.com/fluffynuts/NExpect)에 놀랄 것입니다. , NuGet에서 것입니다. .

(면책 조항 : 저는 NExpect의 저자입니다)


-1

두 문자열을 스트링 화하고 비교

Assert.AreEqual (JSON.stringify (LeftObject), JSON.stringify (RightObject))


-1
//Below works precisely well, Use it.
private void CompareJson()
{
object expected = new object();
object actual = new object();
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var expectedResponse = serializer.Serialize(expected);
var actualResponse = serializer.Serialize(actual);
Assert.AreEqual(expectedResponse, actualResponse);
}

이 코드 스 니펫에 대해 약간의 단기적인 도움을 제공 할 수 있습니다. 적절한 설명 이것이 문제에 대한 좋은 해결책 인지 보여줌으로써 장기적인 가치를 크게 향상시킬 것이며, 다른 비슷한 질문을 가진 미래 독자들에게 더 유용 할 것입니다. 제발 편집 당신이 만든 가정 등 일부 설명을 추가 할 답변을.
Toby Speight

그리고 이것이 Max의 대답에 무엇을 추가 합니까?
Toby Speight
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.