Bandingkan persamaan antara dua objek di NUnit


126

Saya mencoba menegaskan bahwa satu objek "sama" dengan objek lain.

Objek hanya contoh kelas dengan banyak properti publik. Apakah ada cara mudah untuk meminta NUnit menegaskan kesetaraan berdasarkan propertinya?

Ini adalah solusi saya saat ini tetapi saya pikir mungkin ada sesuatu yang lebih baik:

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

Apa yang akan saya lakukan adalah dalam semangat yang sama dengan CollectionEquivalentConstraint di mana NUnit memverifikasi bahwa isi dari dua koleksi adalah sama.

Jawaban:


51

Override. Sama dengan objek Anda dan dalam unit test Anda bisa melakukannya:

Assert.AreEqual(LeftObject, RightObject);

Tentu saja, ini mungkin berarti Anda hanya memindahkan semua perbandingan individu ke metode .Equals, tetapi itu akan memungkinkan Anda untuk menggunakan kembali implementasi itu untuk beberapa tes, dan mungkin masuk akal jika objek tetap dapat membandingkan diri mereka dengan saudara kandung.


2
Terima kasih, lassevk. Ini berhasil untuk saya! Saya menerapkan. Setara sesuai dengan pedoman di sini: msdn.microsoft.com/en-us/library/336aedhh(VS.80).aspx
Michael Haren

12
Dan GetHashCode (), jelas ;-p
Marc Gravell

Nomor 1 pada daftar di halaman itu adalah untuk menimpa GetHashCode, dan dia mengatakan dia mengikuti panduan itu :) Tapi ya, kesalahan umum untuk mengabaikan itu. Biasanya bukan kesalahan yang akan Anda perhatikan sebagian besar waktu, tetapi ketika Anda melakukannya, itu seperti salah satu saat ketika Anda mengatakan "Oh, hei, mengapa ini ular celana saya dan mengapa dia menggigit pantat saya".
Lasse V. Karlsen

1
Satu peringatan penting: jika objek Anda juga mengimplementasikannya IEnumerableakan dibandingkan sebagai koleksi tanpa mengesampingkan implementasi Equalskarena NUnit memberikan IEnumerableprioritas lebih tinggi. Lihat NUnitEqualityComparer.AreEqualmetode untuk detailnya. Anda dapat mengganti pembanding dengan menggunakan salah satu Using()metode kendala kesetaraan . Meski begitu, itu tidak cukup untuk mengimplementasikan non-generik IEqualityComparerkarena NUnit menggunakan adaptor.
Kaleb Pederson

13
Lebih Banyak Peringatan: Menerapkan GetHashCode()pada tipe yang bisa berubah akan berperilaku salah jika Anda pernah menggunakan objek itu sebagai kunci. IMHO, mengesampingkan Equals(), GetHashCode()dan membuat objek tidak berubah hanya untuk pengujian tidak masuk akal.
bavaza

118

Jika Anda tidak dapat mengesampingkan Equals karena alasan apa pun, Anda dapat membangun metode pembantu yang beralih melalui properti publik dengan merefleksikan dan menegaskan setiap properti. Sesuatu seperti ini:

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: ini tidak benar. Type.GetProperties Method: Mengembalikan semua properti publik dari Type saat ini. Lihat msdn.microsoft.com/en-us/library/aky14axb.aspx
Sergii Volchkov

4
Terima kasih. Namun, saya harus mengubah urutan params aktual dan yang diharapkan karena konversi adalah bahwa diharapkan adalah param sebelum aktual.
Valamas

ini adalah pendekatan yang lebih baik IMHO, Equal & HashCode menimpa tidak harus didasarkan pada membandingkan setiap bidang dan ditambah itu sangat membosankan untuk dilakukan di setiap objek. Kerja bagus!
Scott White

3
Ini berfungsi baik jika tipe Anda hanya memiliki tipe dasar sebagai properti. Namun, jika tipe Anda memiliki properti dengan tipe khusus (yang tidak menerapkan Setara) itu akan gagal.
Bobby Cannon

Menambahkan beberapa rekursi untuk properti objek, tetapi saya harus melewati properti yang diindeks:
cerhart

113

Jangan menimpa Sama dengan hanya untuk tujuan pengujian. Itu membosankan dan memengaruhi logika domain. Sebagai gantinya,

Gunakan JSON untuk membandingkan data objek

Tidak ada logika tambahan pada objek Anda. Tidak ada tugas tambahan untuk pengujian.

Cukup gunakan metode sederhana ini:

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

Tampaknya berhasil dengan baik. Info hasil pelari uji akan menampilkan perbandingan string JSON (grafik objek) yang disertakan sehingga Anda dapat melihat secara langsung apa yang salah.

Juga mencatat! Jika Anda memiliki objek kompleks yang lebih besar dan hanya ingin membandingkan bagian-bagiannya, Anda dapat ( menggunakan LINQ untuk data urutan ) membuat objek anonim untuk digunakan dengan metode di atas.

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
Ini adalah cara terbaik untuk menguji, terutama jika Anda berurusan dengan JSON (mis. Menggunakan klien yang diketik untuk mengakses layanan web). Jawaban ini harusnya jauh lebih tinggi.
Roopesh Shenoy

1
Gunakan Linq! @DmitryBLR (lihat paragraf terakhir dalam jawaban) :)
Maks

3
Ini ide yang bagus. Saya akan menggunakan Json.NET yang lebih baru: var expectedJson = Newtonsoft.Json.JsonConvert.SerializeObject (diharapkan);
BrokeMyLegBiking

2
Ini tidak akan berfungsi dengan referensi melingkar. Gunakan github.com/kbilsted/StatePrinter sebagai gantinya untuk pengalaman yang lebih baik atas pendekatan JSON
Carlo V. Dango

2
Itu benar @KokaChernov dan kadang-kadang Anda ingin gagal tes jika pemesanannya tidak sama, tetapi jika Anda tidak ingin gagal jika pemesanan tidak sama, Anda dapat membuat pengurutan eksplisit (menggunakan linq) pada daftar sebelum meneruskannya ke metode AreEqualByJson. Varian sederhana "mengatur ulang" objek Anda sebelum pengujian adalah dalam contoh kode terakhir dalam jawabannya. Jadi itu sangat "universal", saya pikir! :)
Maks

91

Coba pustaka FluentAssertions:

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

http://www.fluentassertions.com/

Itu juga dapat diinstal menggunakan NuGet.


18
ShouldHave telah ditinggalkan, jadi seharusnya dto.ShouldBeEquivalentTo (pelanggan); sebagai gantinya
WhiteKnight

2
Ini adalah jawaban terbaik untuk alasan ini .
Todd Menier

ShouldBeEquivalent is buggy :(
Konstantin Chernov

3
hanya memiliki masalah yang sama dan menggunakan yang berikut yang tampaknya berfungsi dengan baik:actual.ShouldBeEquivalentTo(expected, x => x.ExcludingMissingMembers())
stt106

1
Ini adalah lib yang hebat! Tidak perlu mengesampingkan Persamaan dan juga (jika persamaannya tetap ditimpa, misalnya untuk objek nilai) tidak bergantung pada implementasi yang benar. Perbedaannya juga dicetak dengan baik, seperti halnya Hamcrest untuk Java.
kap

35

Saya lebih suka untuk tidak mengabaikan Equals hanya untuk mengaktifkan pengujian. Jangan lupa bahwa jika Anda mengesampingkan Equals Anda benar-benar harus menimpa GetHashCode juga atau Anda mungkin mendapatkan hasil yang tidak terduga jika Anda menggunakan objek Anda dalam kamus misalnya.

Saya suka pendekatan refleksi di atas karena melayani penambahan properti di masa depan.

Untuk solusi cepat dan sederhana namun seringkali paling mudah untuk membuat metode pembantu yang menguji apakah objeknya sama, atau mengimplementasikan IEqualityComparer di kelas yang Anda jaga pribadi untuk tes Anda. Saat menggunakan solusi IEqualityComparer Anda tidak perlu repot dengan implementasi GetHashCode. Sebagai contoh:

// 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.");
    }
}

Persamaannya tidak menangani nilai null. Saya akan menambahkan yang berikut sebelum pernyataan pengembalian Anda dalam metode equals. if (x == null && y == null) {return true; } if (x == null || y == null) {return false; } Saya mengedit pertanyaan untuk menambahkan dukungan nol.
Bobby Cannon

Tidak berfungsi untuk saya dengan membuang NotImplementedException (); di GetHashCode. Mengapa saya perlu fungsi itu di IEqualityComparer?
love2code

15

Saya sudah mencoba beberapa pendekatan yang disebutkan di sini. Sebagian besar melibatkan serialisasi objek Anda dan melakukan perbandingan string. Meskipun super mudah dan umumnya sangat efektif, saya menemukan sedikit kekurangan ketika Anda mengalami kegagalan dan sesuatu seperti ini dilaporkan:

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

Mencari tahu di mana perbedaan itu merupakan rasa sakit untuk sedikitnya.

Dengan perbandingan grafik objek FluentAssertions ' (yaitu a.ShouldBeEquivalentTo(b)), Anda mendapatkan ini kembali:

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

Itu jauh lebih baik. Dapatkan FluentAssertions sekarang, Anda akan senang nanti (dan jika Anda memperbaiki ini, harap juga upvote jawaban dkl di mana FluentAssertions pertama kali disarankan).


9

Saya setuju dengan ChrisYoxall - menerapkan Equals dalam kode utama Anda murni untuk tujuan pengujian tidak baik.

Jika Anda menerapkan Equals karena beberapa logika aplikasi mengharuskannya, maka itu tidak masalah, tetapi jauhkan kode hanya pengujian murni dari hal-hal yang berantakan (juga semantik pemeriksaan yang sama untuk pengujian mungkin berbeda dari yang dibutuhkan aplikasi Anda).

Singkatnya, jauhkan kode khusus pengujian dari kelas Anda.

Perbandingan dangkal sederhana dari properti yang menggunakan refleksi harus cukup untuk sebagian besar kelas, meskipun Anda mungkin perlu berulang jika objek Anda memiliki sifat kompleks. Jika mengikuti referensi, waspadalah terhadap referensi melingkar atau serupa.

Licik


Tangkapan bagus pada referensi melingkar. Mudah diatasi jika Anda menyimpan kamus objek di pohon perbandingan.
Lucas B

6

Kendala properti , ditambahkan dalam NUnit 2.4.2, memungkinkan solusi yang lebih mudah dibaca daripada yang asli OP, dan menghasilkan pesan kegagalan yang jauh lebih baik. Ini tidak generik, tetapi jika Anda tidak perlu melakukannya untuk kelas terlalu banyak, itu solusi yang sangat memadai.

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

Bukan sebagai tujuan umum sebagai implementasi Equalstetapi memberikan pesan kegagalan yang jauh lebih baik daripada

Assert.AreEqual(ExpectedObject, ActualObject);

4

Solusi JSON Max Wikstrom (di atas) paling masuk akal bagi saya, singkat, bersih, dan yang terpenting berfungsi. Secara pribadi saya lebih suka untuk mengimplementasikan konversi JSON sebagai metode terpisah dan menempatkan pernyataan kembali di dalam unit test seperti ini ...

METODE BANTUAN:

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

UJI UNIT:

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

FYI - Anda mungkin perlu menambahkan referensi ke System.Web.Extensions dalam solusi Anda.


4

Ini adalah utas yang cukup lama tetapi saya bertanya-tanya apakah ada alasan mengapa tidak ada jawaban yang diajukan NUnit.Framework.Is.EqualTodanNUnit.Framework.Is.NotEqualTo ?

Seperti:

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

dan

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

4
Karena tidak mencetak detail apa yang berbeda
Shrage Smilowitz

1

Pilihan lain adalah untuk menulis batasan kustom dengan mengimplementasikan Constraintkelas abstrak NUnit . Dengan kelas penolong untuk menyediakan sedikit gula sintaksis, kode uji yang dihasilkan menyenangkan dan mudah dibaca misalnya

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

Sebagai contoh ekstrem, anggap kelas yang memiliki anggota 'baca-saja' tidak IEquatable, dan Anda tidak dapat mengubah kelas yang sedang diuji bahkan jika Anda ingin:

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}}" 
        };
    }
}

Kontrak untuk Constraintkelas mengharuskan seseorang untuk menimpa Matchesdan WriteDescriptionTo(dalam kasus ketidakcocokan, narasi untuk nilai yang diharapkan) tetapi juga mengesampingkan WriteActualValueTo(narasi untuk nilai aktual) masuk akal:

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

Ditambah kelas pembantu:

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

Contoh penggunaan:

[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

Saya akan membangun jawaban dari @Juanma. Namun, saya percaya ini tidak boleh diimplementasikan dengan pernyataan unit test. Ini adalah utilitas yang dapat digunakan dalam beberapa keadaan dengan kode non-tes.

Saya menulis artikel tentang hal ini http://timoch.com/blog/2013/06/unit-test-equality-is-not-domain-equality/

Proposal saya adalah sebagai berikut:

/// <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();
} 

Menggunakan ini dengan NUnit

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

menghasilkan pesan berikut pada ketidakcocokan.

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 telah ditulis khusus untuk membuang grafik objek ke representasi string dengan tujuan menulis unit test yang mudah.

  • Muncul dengan metode Assert yang mengeluarkan string yang mudah lolos copy-paste ke dalam tes untuk memperbaikinya.
  • Ini memungkinkan unittest untuk secara otomatis ditulis ulang
  • Ini terintegrasi dengan semua kerangka kerja pengujian unit
  • Tidak seperti serialisasi JSON, referensi melingkar didukung
  • Anda dapat dengan mudah memfilter, jadi hanya sebagian tipe yang dibuang

Diberikan

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

Anda dapat mengetik dengan cara yang aman, dan menggunakan penyelesaian otomatis studio visual termasuk atau mengecualikan bidang.

  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

Cukup instal ExpectedObjects dari Nuget, Anda dapat dengan mudah membandingkan nilai properti dua objek, setiap nilai objek koleksi, dua nilai objek yang dikomposisikan dan membandingkan sebagian nilai properti dengan jenis anonim.

Saya punya beberapa contoh di github: https://github.com/hatelove/CompareObjectEquals

Berikut adalah beberapa contoh yang berisi skenario membandingkan objek:

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

Referensi:

  1. ExpectedObjects github
  2. PengantarObjek yang Diharapkan


1

Saya telah mengakhiri dengan menulis sebuah pabrik ekspresi sederhana:

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

dan gunakan saja:

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

Ini sangat berguna karena saya harus membandingkan koleksi benda-benda tersebut. Dan Anda dapat menggunakan pembanding ini di tempat lain :)

Berikut adalah intinya dengan contoh: https://gist.github.com/Pzixel/b63fea074864892f9aba8ffde312094f


0

Deserialize kedua kelas, dan lakukan perbandingan string.

EDIT: Berfungsi sempurna, ini adalah output yang saya dapatkan dari 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)

EDIT DUA: Kedua objek bisa identik, tetapi urutan properti serial tidak sama. Karena itu XML berbeda. DOH!

EDIT TIGA: Ini berhasil. Saya menggunakannya dalam tes saya. Tetapi Anda harus menambahkan item ke properti koleksi dalam urutan kode yang diuji menambahkannya.


1
cerita bersambung ? Ide yang menarik. Saya tidak yakin bagaimana itu akan bertahan dalam hal kinerja,
Michael Haren

tidak akan memungkinkan Anda untuk membandingkan ganda atau desimal dengan presisi yang diberikan.
Noctis

0

Saya tahu ini adalah pertanyaan yang sangat lama, tetapi NUnit masih belum memiliki dukungan asli untuk ini. Namun, jika Anda menyukai pengujian gaya BDD (ala Jasmine), Anda akan terkejut dengan NExpect ( https://github.com/fluffynuts/NExpect , dapatkan dari NuGet), yang memiliki pengujian kesetaraan mendalam yang dipanggang tepat di sana .

(Penafian: Saya penulis NExpect)


-1

Merangkan dan membandingkan dua string

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

Terima kasih atas cuplikan kode ini, yang mungkin memberikan bantuan jangka pendek terbatas. Penjelasan yang tepat akan sangat meningkatkan nilai jangka panjangnya dengan menunjukkan mengapa ini adalah solusi yang baik untuk masalah ini, dan akan membuatnya lebih bermanfaat bagi pembaca masa depan dengan pertanyaan lain yang serupa. Harap edit jawaban Anda untuk menambahkan beberapa penjelasan, termasuk asumsi yang Anda buat.
Toby Speight

Dan apa yang ini tambahkan ke jawaban Max ?
Toby Speight
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.