So sánh sự bình đẳng giữa hai đối tượng trong NUnit


126

Tôi đang cố gắng khẳng định rằng một đối tượng "bằng" với một đối tượng khác.

Các đối tượng chỉ là các thể hiện của một lớp với một loạt các thuộc tính công cộng. Có cách nào dễ dàng để NUnit khẳng định sự bình đẳng dựa trên các thuộc tính không?

Đây là giải pháp hiện tại của tôi nhưng tôi nghĩ có thể có một cái gì đó tốt hơn:

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

Những gì tôi sẽ làm sẽ có cùng tinh thần với CollectionEquivalentConstraint trong đó NUnit xác minh rằng nội dung của hai bộ sưu tập là giống hệt nhau.

Câu trả lời:


51

Ghi đè .Equals cho đối tượng của bạn và trong bài kiểm tra đơn vị, sau đó bạn có thể chỉ cần làm điều này:

Assert.AreEqual(LeftObject, RightObject);

Tất nhiên, điều này có thể có nghĩa là bạn chỉ cần di chuyển tất cả các so sánh riêng lẻ sang phương thức .Equals, nhưng nó sẽ cho phép bạn sử dụng lại việc thực hiện đó cho nhiều thử nghiệm và có thể có ý nghĩa nếu các đối tượng có thể so sánh bản thân với anh chị em.


2
Cảm ơn, lassevk. Điều này làm việc cho tôi! Tôi đã triển khai .Equals theo các hướng dẫn ở đây: msdn.microsoft.com/en-us/l Library / 33aedhh (VS.80) .aspx
Michael Haren

12
Và GetHashCode (), rõ ràng ;-p
Marc Gravell

Số 1 trong danh sách trên trang đó là ghi đè GetHashCode, và anh ta đã nói rằng anh ta đã làm theo các hướng dẫn đó :) Nhưng vâng, lỗi phổ biến để bỏ qua điều đó. Thường thì không phải là một lỗi bạn sẽ chú ý, nhưng khi bạn làm vậy, nó giống như một trong những lần bạn nói "Ồ, này, tại sao con rắn này lại mặc quần của tôi và tại sao anh ta lại cắn vào mông tôi".
Lasse V. Karlsen

1
Một cảnh báo quan trọng: nếu đối tượng của bạn cũng thực hiện IEnumerablethì nó sẽ được so sánh như một bộ sưu tập bất kể việc triển khai vượt trội Equalsvì NUnit có IEnumerablemức độ ưu tiên cao hơn. Xem các NUnitEqualityComparer.AreEqualphương pháp để biết chi tiết. Bạn có thể ghi đè lên bộ so sánh bằng cách sử dụng một trong các Using()phương thức của ràng buộc đẳng thức. Ngay cả khi đó, nó không đủ để thực hiện cái không chung chung IEqualityComparerdo bộ điều hợp NUnit sử dụng.
Kaleb Pederson

13
Cẩn thận hơn: Việc thực hiện GetHashCode()trên các loại có thể thay đổi sẽ hoạt động sai nếu bạn sử dụng đối tượng đó làm khóa. IMHO, trọng Equals(), GetHashCode()và làm cho bất biến đối tượng chỉ để thử nghiệm không có ý nghĩa.
bavaza

118

Nếu bạn không thể ghi đè Bằng vì bất kỳ lý do nào, bạn có thể xây dựng phương thức trợ giúp lặp lại thông qua các thuộc tính công cộng bằng cách phản ánh và khẳng định từng thuộc tính. Một cái gì đó như thế này:

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: điều này không đúng Phương thức Type.GetProperIES: Trả về tất cả các thuộc tính công khai của Loại hiện tại. Xem msdn.microsoft.com/en-us/l
Library / like14axb.aspx

4
cảm ơn. tuy nhiên, tôi đã phải chuyển đổi thứ tự của các thông số thực tế và dự kiến ​​vì sự hội tụ là dự kiến ​​là một thông số trước khi thực tế.
Valamas

đây là cách tiếp cận tốt hơn IMHO, Equal & HashCode ghi đè không cần phải dựa trên việc so sánh mọi lĩnh vực và cộng với đó là rất tẻ nhạt để làm trên mọi đối tượng. Làm tốt lắm!
Scott White

3
Điều này hoạt động rất tốt nếu loại của bạn chỉ có các loại cơ bản là thuộc tính. Tuy nhiên, nếu loại của bạn có các thuộc tính với các loại tùy chỉnh (không triển khai Bằng) thì nó sẽ thất bại.
Bobby Cannon

Đã thêm một số đệ quy cho các thuộc tính đối tượng, nhưng tôi đã phải bỏ qua các thuộc tính được lập chỉ mục:
cerhart

113

Không ghi đè Bằng chỉ cho mục đích thử nghiệm. Thật tẻ nhạt và ảnh hưởng đến logic miền. Thay thế,

Sử dụng JSON để so sánh dữ liệu của đối tượng

Không có logic bổ sung trên các đối tượng của bạn. Không có nhiệm vụ bổ sung để thử nghiệm.

Chỉ cần sử dụng phương pháp đơn giản này:

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

Nó dường như làm việc tuyệt vời. Thông tin kết quả của người chạy thử nghiệm sẽ hiển thị so sánh chuỗi JSON (biểu đồ đối tượng) được bao gồm để bạn thấy trực tiếp những gì sai.

Cũng lưu ý! Nếu bạn có các đối tượng phức tạp lớn hơn và chỉ muốn so sánh các phần của chúng, bạn có thể ( sử dụng LINQ cho dữ liệu chuỗi ) tạo các đối tượng ẩn danh để sử dụng với phương thức trên.

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
Đây là một cách tuyệt vời để kiểm tra, đặc biệt là nếu bạn đang xử lý JSON (ví dụ: sử dụng máy khách đã nhập để truy cập dịch vụ web). Câu trả lời này nên cao hơn nhiều.
Roopesh Shenoy

1
Sử dụng Linq! @DmitryBLR (xem đoạn cuối trong câu trả lời) :)
Tối đa

3
Ý kiến ​​hay. Tôi sẽ sử dụng Json.NET mới hơn: var lookingJson = Newtonsoft.Json.JsonConvert.SerializeObject (dự kiến);
BrokeMyLegBiking 14/11 '

2
Điều này sẽ không làm việc với các tài liệu tham khảo tròn. Thay vào đó, hãy sử dụng github.com/kbilsted/StatePrinter để cải thiện trải nghiệm về phương pháp JSON
Carlo V. Dango

2
Đó là sự thật @KokaCécov và đôi khi bạn muốn thử nghiệm nếu không đặt hàng giống nhau nhưng nếu bạn không muốn thất bại nếu việc đặt hàng không giống nhau, bạn có thể sắp xếp rõ ràng (sử dụng linq) trong danh sách trước khi chuyển chúng sang phương thức AreEqualByJson. Một biến thể đơn giản của "sắp xếp lại" các đối tượng của bạn trước khi thử nghiệm là ví dụ mã cuối cùng trong câu trả lời. Vì vậy, đó là rất "phổ quát" tôi nghĩ! :)
Tối đa

91

Hãy thử thư viện FluentAssertions:

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

http://www.fluentassertions.com/

Nó cũng có thể được cài đặt bằng NuGet.


18
ShouldHave đã không được chấp nhận, vì vậy nên là dto.ShouldBeEquivalentTo (khách hàng); thay vào đó
WhiteKnight

2
Đây là câu trả lời tốt nhất cho lý do này .
Todd Menier

ShouldBeEquivalent là lỗi :(
Konstantin Chernov

3
chỉ có cùng một vấn đề và sử dụng như sau có vẻ hoạt động tốt:actual.ShouldBeEquivalentTo(expected, x => x.ExcludingMissingMembers())
stt106

1
Đây là một lib tuyệt vời! Không yêu cầu ghi đè Bằng và dù sao (nếu bằng được ghi đè bằng mọi cách, ví dụ: đối với các đối tượng giá trị) không dựa vào việc thực hiện đúng. Ngoài ra, sự khác biệt được in độc đáo, giống như Hamcrest dành cho Java.
kap

35

Tôi không muốn ghi đè Bằng chỉ để cho phép thử nghiệm. Đừng quên rằng nếu bạn ghi đè Bằng, bạn thực sự nên ghi đè GetHashCode hoặc bạn có thể nhận được kết quả không mong muốn nếu bạn đang sử dụng các đối tượng của mình trong từ điển chẳng hạn.

Tôi thích cách tiếp cận phản chiếu ở trên vì nó phục vụ cho việc bổ sung các thuộc tính trong tương lai.

Tuy nhiên, đối với một giải pháp đơn giản và nhanh chóng, thường dễ nhất là tạo một phương thức trợ giúp để kiểm tra xem các đối tượng có bằng nhau hay không, hoặc triển khai IEqualityComparer trên một lớp mà bạn giữ riêng cho các bài kiểm tra của mình. Khi sử dụng giải pháp IEqualityComparer, bạn không cần bận tâm đến việc triển khai GetHashCode. Ví dụ:

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

Các bằng không xử lý các giá trị null. Tôi sẽ thêm phần sau vào câu lệnh return của bạn trong phương thức equals. if (x == null && y == null) {trả về true; } if (x == null | | y == null) {return false; } Tôi đã chỉnh sửa câu hỏi để thêm hỗ trợ null.
Bobby Cannon

Không hoạt động với tôi với ném NotImcellenceedException () mới; trong mã GetHashCode. Tại sao tôi cần chức năng đó trong IEqualityComparer?
love2code

15

Tôi đã thử một vài cách tiếp cận được đề cập ở đây. Hầu hết liên quan đến việc tuần tự hóa các đối tượng của bạn và thực hiện một chuỗi so sánh. Mặc dù siêu dễ và thường rất hiệu quả, tôi đã thấy nó xuất hiện một chút khi bạn gặp thất bại và một cái gì đó như thế này được báo cáo:

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

Tìm ra nơi khác biệt là một nỗi đau để nói rằng ít nhất.

Với các so sánh biểu đồ đối tượng của FluentAssertions (nghĩa là a.ShouldBeEquivalentTo(b)), bạn sẽ lấy lại được điều này:

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

Điều đó đẹp hơn nhiều. Nhận FluentAssertions ngay bây giờ, bạn sẽ vui mừng sau này (và nếu bạn nêu lên điều này, vui lòng nêu lên câu trả lời của dkl trong đó FluentAssertions được đề xuất lần đầu tiên).


9

Tôi đồng ý với ChrisYoxall - thực hiện Equals trong mã chính của bạn hoàn toàn cho mục đích thử nghiệm là không tốt.

Nếu bạn đang triển khai Bằng vì một số logic ứng dụng yêu cầu điều đó, thì tốt thôi, nhưng hãy giữ mã chỉ thử nghiệm thuần túy khỏi sự lộn xộn (cũng như ngữ nghĩa của việc kiểm tra tương tự để kiểm tra có thể khác với những gì ứng dụng của bạn yêu cầu).

Nói tóm lại, hãy tiếp tục kiểm tra mã chỉ ra khỏi lớp của bạn.

So sánh nông cạn đơn giản bằng cách sử dụng sự phản chiếu là đủ cho hầu hết các lớp, mặc dù bạn có thể cần phải lặp lại nếu các đối tượng của bạn có các thuộc tính phức tạp. Nếu tham khảo sau, hãy cẩn thận của tài liệu tham khảo tròn hoặc tương tự.

Sly


Đẹp bắt trên tài liệu tham khảo tròn. Dễ dàng khắc phục nếu bạn giữ một từ điển các đối tượng đã có trong cây so sánh.
Lucas B

6

Các ràng buộc thuộc tính , được thêm vào trong NUnit 2.4.2, cho phép một giải pháp dễ đọc hơn so với bản gốc của OP và nó tạo ra các thông báo lỗi tốt hơn nhiều. Nó không phải là chung chung, nhưng nếu bạn không cần phải làm điều đó cho quá nhiều lớp, thì đó là một giải pháp rất phù hợp.

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

Không phải là mục đích chung như thực hiện Equalsnhưng nó mang lại một thông báo thất bại tốt hơn nhiều so với

Assert.AreEqual(ExpectedObject, ActualObject);

4

Giải pháp JSON của Max Wikstrom (ở trên) có ý nghĩa nhất đối với tôi, đó là ngắn gọn, sạch sẽ và quan trọng nhất là nó hoạt động. Cá nhân mặc dù tôi muốn thực hiện chuyển đổi JSON như một phương thức riêng biệt và đặt khẳng định trở lại bên trong bài kiểm tra đơn vị như thế này ...

PHƯƠNG PHÁP HPORT TRỢ:

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

KIỂM TRA ĐƠN VỊ:

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 - Bạn có thể cần thêm một tham chiếu đến System.Web.Extensions trong giải pháp của mình.


4

Đây là một chủ đề khá cũ nhưng tôi đã tự hỏi nếu có một lý do tại sao không có câu trả lời đề xuất NUnit.Framework.Is.EqualToNUnit.Framework.Is.NotEqualTo?

Nhu la:

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

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

4
Bởi vì nó không in ra những chi tiết khác biệt
Shrage Smilowitz

1

Một tùy chọn khác là viết một ràng buộc tùy chỉnh bằng cách triển khai Constraintlớp trừu tượng NUnit . Với một lớp trợ giúp để cung cấp một ít đường cú pháp, mã kiểm tra kết quả thật ngắn gọn và dễ đọc, ví dụ

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

Đối với một ví dụ cực đoan, hãy xem xét lớp có thành viên 'chỉ đọc' IEquatable, và bạn không thể thay đổi lớp đang kiểm tra ngay cả khi bạn muốn:

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

Hợp đồng cho Constraintlớp yêu cầu một người ghi đè MatchesWriteDescriptionTo(trong trường hợp không khớp, tường thuật cho giá trị mong đợi) nhưng cũng ghi đè WriteActualValueTo(tường thuật cho giá trị thực) có ý nghĩa:

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

Cộng với lớp người trợ giúp:

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

Ví dụ sử dụng:

[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

Tôi sẽ dựa trên câu trả lời của @Juanma. Tuy nhiên, tôi tin rằng điều này không nên được thực hiện với các xác nhận kiểm tra đơn vị. Đây là một tiện ích rất có thể được sử dụng trong một số trường hợp bởi mã không thử nghiệm.

Tôi đã viết một bài viết về vấn đề này http://timoch.com/blog/2013/06/unit-test-equality-is-not-domain-equality/

Đề xuất của tôi là như sau:

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

Sử dụng cái này với NUnit

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

mang lại thông điệp sau đây về sự không phù hợp.

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 đã được viết riêng để kết xuất đồ thị đối tượng vào biểu diễn chuỗi với mục đích viết các bài kiểm tra đơn vị dễ dàng.

  • Nó đi kèm với các phương thức Khẳng định tạo ra một chuỗi thoát đúng cách dễ dàng sao chép-dán vào kiểm tra để sửa nó.
  • Nó cho phép unittest được tự động viết lại
  • Nó tích hợp với tất cả các khung thử nghiệm đơn vị
  • Không giống như tuần tự hóa JSON, các tham chiếu vòng tròn được hỗ trợ
  • Bạn có thể dễ dàng lọc, vì vậy chỉ các phần của loại được đổ

Được

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

Theo cách an toàn, bạn có thể sử dụng tự động hoàn thành phòng thu trực quan bao gồm hoặc loại trừ các trường.

  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

Chỉ cần cài đặt ExpectedObjects từ Nuget, bạn có thể dễ dàng so sánh giá trị thuộc tính của hai đối tượng, từng giá trị đối tượng của bộ sưu tập, hai giá trị của đối tượng được cấu thành và giá trị thuộc tính so sánh một phần theo loại ẩn danh.

Tôi có một số ví dụ trên github: https://github.com/hatelove/CompareObjectEquals

Dưới đây là một số ví dụ có chứa các kịch bản so sánh đối tượng:

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

Tài liệu tham khảo:

  1. Dự kiến ​​github
  2. Giới thiệu ExpectedObjects


1

Tôi đã kết thúc bằng việc viết một nhà máy biểu hiện đơn giản:

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

và chỉ sử dụng nó:

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

Nó rất hữu ích vì tôi phải so sánh bộ sưu tập các đối tượng như vậy. Và bạn có thể sử dụng so sánh này ở một nơi khác :)

Dưới đây là ý chính với ví dụ: https://gist.github.com/Pzixel/b63fea074864892f9aba8ffde312094f


0

Giải trừ cả hai lớp và so sánh chuỗi.

EDIT: Hoạt động hoàn hảo, đây là đầu ra tôi nhận được từ 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 HAI: Hai đối tượng có thể giống hệt nhau, nhưng thứ tự các thuộc tính được tuần tự hóa không giống nhau. Do đó, XML là khác nhau. ĐỪNG!

EDIT BA: Điều này không hoạt động. Tôi đang sử dụng nó trong các thử nghiệm của tôi. Nhưng bạn phải thêm các mục vào thuộc tính bộ sưu tập theo thứ tự mã được kiểm tra thêm chúng.


1
nối tiếp ? Ý tưởng thú vị. Mặc dù vậy, tôi không chắc chắn về cách thức hoạt động của nó về mặt hiệu suất
Michael Haren

sẽ không cho phép bạn so sánh số nhân hoặc số thập phân với độ chính xác cho trước.
Noctis

0

Tôi biết đây là một câu hỏi thực sự cũ, nhưng NUnit vẫn không có hỗ trợ riêng cho việc này. Tuy nhiên, nếu bạn thích thử nghiệm theo phong cách BDD (ala Jasmine), bạn sẽ ngạc nhiên với NExpect ( https://github.com/fluffynuts/NExpect , hãy lấy nó từ NuGet), có thử nghiệm công bằng sâu sắc ngay trong đó .

(từ chối trách nhiệm: Tôi là tác giả của NExpect)


-1

Chuỗi và so sánh hai chuỗi

Assert.AreEqual (JSON.opesify (LeftObject), JSON.opesify (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);
}

Cảm ơn bạn vì đoạn mã này, có thể cung cấp một số trợ giúp ngắn hạn có giới hạn. Một lời giải thích phù hợp sẽ cải thiện đáng kể giá trị lâu dài của nó bằng cách chỉ ra lý do tại sao đây là một giải pháp tốt cho vấn đề và sẽ giúp nó hữu ích hơn cho những người đọc tương lai với những câu hỏi tương tự khác. Vui lòng chỉnh sửa câu trả lời của bạn để thêm một số giải thích, bao gồm các giả định bạn đã thực hiện.
Toby Speight

Và điều này thêm gì vào câu trả lời của Max ?
Toby Speight
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.