NUnitの2つのオブジェクト間の等価性を比較する


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が2つのコレクションの内容が同一であることを確認するCollectionEquivalentConstraintと同じ精神です。

回答:


51

オブジェクトの.Equalsをオーバーライドし、単体テストでこれを行うだけです。

Assert.AreEqual(LeftObject, RightObject);

もちろん、これはすべての個別の比較を.Equalsメソッドに移動することを意味するかもしれませんが、その実装を複数のテストに再利用できるようにし、オブジェクトが兄弟と比較できるようにする必要がある場合におそらく意味があります。


2
ありがとう、lassevk。これは私のために働いた!次のガイドラインに従って.Equalsを実装しました:msdn.microsoft.com/en-us/library/336aedhh(
Michael Haren

12
そして、GetHashCode()、明らかに;-p
Marc Gravell

そのページのリストの1番目は、GetHashCodeをオーバーライドすることであり、彼はそれらのガイドラインに従っていると言っていました:)しかし、そうです、それを無視する一般的な間違い。通常、ほとんどの場合は気づく間違いではありませんが、そうするとき、「ああ、ねえ、なぜこれが私のズボンを蛇にして、なぜ彼が私のお尻を噛んでいるのか」と言うときのようなものです。
Lasse V. Karlsen、

1
重要な注意点の1つ:オブジェクトが実装されIEnumerableている場合、EqualsNUnitのIEnumerable方が優先されるため、の実装のオーバーライドに関係なく、オブジェクトはコレクションとして比較されます。詳細については、NUnitEqualityComparer.AreEqualメソッドを参照してください。等式制約のUsing()メソッドの1つを使用して、コンパレータをオーバーライドできます。それでも、IEqualityComparerNUnitが使用するアダプターのため、非ジェネリックを実装するだけでは不十分です。
Kaleb Pederson 2013年

13
GetHashCode()注意点:オブジェクトをキーとして使用した場合、変更可能な型に実装すると誤動作します。IMHO、オーバーライドEquals()GetHashCode()およびテストのためだけにオブジェクトを不変にすることは意味がありません。
bavaza 2013

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

@wesley:これは真実ではありません。Type.GetPropertiesメソッド:現在のTypeのすべてのパブリックプロパティを返します。msdn.microsoft.com/en-us/library/aky14axb.aspxを
Sergii Volchkov

4
ありがとう。しかし、実際には予想されるパラメータが実際のパラメータより前であるため、実際のパラメータと予想されるパラメータの順序を切り替える必要がありました。
Valamas 2011

これは、IMHO、Equal、HashCodeのオーバーライドがすべてのフィールドの比較に基づいている必要はなく、さらに、すべてのオブジェクトにわたって行うのは非常に面倒な方法です。よくやった!
スコットホワイト

3
これは、タイプにプロパティとして基本的なタイプしかない場合に最適です。ただし、タイプにカスタムタイプのプロパティ(Equalsを実装していない)がある場合、失敗します。
ボビーキャノン

オブジェクトのプロパティのためのいくつかの再帰を追加しましたが、私は、インデックス付きプロパティをスキップしなければならなかった:
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を処理している場合(特に、型付きクライアントを使用してWebサービスにアクセスする場合)にテストする優れた方法です。この答えははるかに高いはずです。
Roopesh Shenoy 2013

1
Linqを使用してください!@DmitryBLR(回答の最後の段落を参照):)
最大の

3
これは素晴らしいアイデアです。私は新しいJson.NETを使用します:var expectedJson = Newtonsoft.Json.JsonConvert.SerializeObject(expected);
BrokeMyLegBiking 2014年

2
これは循環参照では機能しません。代わりにgithub.com/kbilsted/StatePrinterを使用して、JSONアプローチよりもエクスペリエンスを向上させてください
Carlo V.

2
それは本当の@KokaChernovであり、順序が同じでない場合にテストを失敗させたいことがありますが、順序が同じでない場合に失敗させたくない場合は、AreEqualByJsonメソッドに渡す前にリストで明示的な並べ替え(linqを使用)を行うことができます。テストの前にオブジェクトを「再配置」する簡単なバリエーションは、答えの最後のコード例にあります。それはとても「普遍的」だと思います!:)
最大の

91

FluentAssertionsライブラリを試してください:

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

http://www.fluentassertions.com/

NuGetを使用してインストールすることもできます。


18
ShouldHaveは廃止されているため、dto.ShouldBeEquivalentTo(customer); 代わりに
WhiteKnight 2014年

2
これがこの理由のための最良の答えです。
トッドメニア2015

ShouldBeEquivalentはバギーです:(
Konstantin Chernov

3
ちょうど同じ問題を抱えていたし、正常に動作しているようだ、次の使用:actual.ShouldBeEquivalentTo(expected, x => x.ExcludingMissingMembers())
stt106

1
これは素晴らしいライブラリです!Equalsをオーバーライドする必要はありません。また、(たとえば、値オブジェクトの場合は、とにかくオーバーライドされている場合は)正しい実装に依存しません。また、JavaのHamcrestのように、違いがうまく表示されます。
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){falseを返す; 質問を編集してnullサポートを追加しました。
ボビーキャノン

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変換を別のメソッドとして実装し、このようにユニットテスト内にアサートを配置したいのですが...

ヘルパーメソッド:

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 Smilowi​​tz、2017年

1

もう1つのオプションは、NUnit抽象Constraintクラスを実装してカスタム制約を作成することです。少し構文糖を提供するヘルパークラスを使用すると、結果のテストコードは心地よく簡潔で読みやすくなります。

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

極端な例として、「読み取り専用」メンバーを持ち、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は、簡単な単体テストを作成することを目的として、オブジェクトグラフを文字列表現にダンプするように特別に作成されています。

  • これは、適切にエスケープされた文字列を簡単にコピーアンドペーストしてテストに出力し、修正する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をインストールするだけで、2つのオブジェクトのプロパティ値、コレクションの各オブジェクト値、2つの合成オブジェクトの値、および匿名型による部分比較プロパティ値を簡単に比較できます。

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. ExpectedObjects github
  2. ExpectedObjectsの紹介


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

このようなオブジェクトのコレクションを比較する必要があるので、非常に便利です。そして、あなたはどこかでこのcompareを使用することができます:)

ここに例の要点があります: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)

2つ編集: 2つのオブジェクトは同一にすることができますが、プロパティがシリアル化される順序は同じではありません。したがって、XMLは異なります。DOH!

3つ編集: これは機能します。テストで使用しています。ただし、テスト対象のコードがアイテムを追加する順序で、コレクションプロパティにアイテムを追加する必要があります。


1
シリアライズ?面白いアイデア。私はそれが、パフォーマンスの面でホールドアップする方法をわからない
マイケル・ハーレン

doubleまたはdecimalsを指定された精度で比較することはできません。
Noctis、

0

私はこれが本当に古い質問であることを知っていますが、NUnitはまだこれをネイティブでサポートしていません。ただし、BDDスタイルのテスト(ala Jasmine)が好きな場合は、深い等価性テストがすぐに行われているNExpect(https://github.com/fluffynuts/NExpect、NuGetから取得)に驚かれることでしょう。。

(免責事項:私はNExpectの作成者です)


-1

2つの文字列を文字列化して比較する

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

そして、これはマックスの答えに何を追加しますか?
Toby Speight
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.