Compare a igualdade entre dois objetos no NUnit

126

Estou tentando afirmar que um objeto é "igual" a outro objeto.

Os objetos são apenas instâncias de uma classe com várias propriedades públicas. Existe uma maneira fácil de o NUnit afirmar a igualdade com base nas propriedades?

Esta é a minha solução atual, mas acho que pode haver algo melhor:

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

O que eu pretendo seria no mesmo espírito que o CollectionEquivalentConstraint, em que o NUnit verifica se o conteúdo de duas coleções é idêntico.

Michael Haren
fonte

Respostas:

51

Substituir .Equals para seu objeto e, no teste de unidade, você pode simplesmente fazer o seguinte:

Assert.AreEqual(LeftObject, RightObject);

Obviamente, isso pode significar que você apenas move todas as comparações individuais para o método .Equals, mas permitiria reutilizar essa implementação para vários testes e provavelmente faz sentido ter se os objetos puderem se comparar com os irmãos de qualquer maneira.

Lasse V. Karlsen
fonte
2
Obrigado, lassevk. Isso funcionou para mim! Eu implementei .Equals acordo com as diretrizes aqui: msdn.microsoft.com/en-us/library/336aedhh(VS.80).aspx
Michael Haren
12
E GetHashCode (), obviamente ;-p
Marc Gravell
O número 1 na lista dessa página é substituir o GetHashCode, e ele disse que seguiu essas diretrizes :) Mas sim, erro comum em ignorar isso. Normalmente, não é um erro que você notará na maioria das vezes, mas quando o faz, é como um daqueles momentos em que você diz "Oh, ei, por que essa cobra está subindo nas minhas calças e por que ele está mordendo minha bunda".
Lasse V. Karlsen
1
Uma ressalva importante: se o seu objeto também implementá- IEnumerablelo, ele será comparado como uma coleção, independentemente das implementações substituídas, Equalsporque o NUnit oferece IEnumerablemaior precedência. Veja os NUnitEqualityComparer.AreEqualmétodos para detalhes. Você pode substituir o comparador usando um dos Using()métodos da restrição de igualdade . Mesmo assim, não é suficiente implementar o não genérico IEqualityComparerdevido ao adaptador que o NUnit usa.
quer
13
Mais informações: a implementação GetHashCode()de tipos mutáveis ​​se comportará mal se você usar esse objeto como chave. IMHO, substituindo Equals(), GetHashCode()e tornando o objeto imutável apenas para testar não faz sentido.
bavaza
118

Se você não puder substituir Igual por qualquer motivo, poderá criar um método auxiliar que itere pelas propriedades públicas por reflexão e afirme cada propriedade. Algo assim:

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]);
    }
}
Juanma
fonte
@ Wesley: isso não é verdade. Método Type.GetProperties: Retorna todas as propriedades públicas do Type atual. Consulte msdn.microsoft.com/en-us/library/aky14axb.aspx
Sergii Volchkov
4
obrigado. no entanto, tive que mudar a ordem dos parâmetros reais e esperados, pois a conversão é que o esperado é um parâmetro antes do real.
Valamas
Essa é uma abordagem melhor. As substituições de IMHO, Equal e HashCode não devem se basear na comparação de todos os campos e, além disso, é muito tedioso fazer com todos os objetos. Bom trabalho!
13118 Scott White
3
Isso funciona muito bem se o seu tipo tem apenas tipos básicos como propriedades. No entanto, se seu tipo tiver propriedades com tipos personalizados (que não implementam Equals), ele falhará.
Bobby Cannon
Adicionado alguns recursão para propriedades do objeto, mas eu tive que pular propriedades ao longo indexados:
cerhart
113

Não substitua Igual apenas para fins de teste. É tedioso e afeta a lógica do domínio. Em vez de,

Use JSON para comparar os dados do objeto

Nenhuma lógica adicional em seus objetos. Nenhuma tarefa extra para teste.

Basta usar este método simples:

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

Parece funcionar muito bem. As informações dos resultados do executor de teste mostrarão a comparação de cadeias JSON (o gráfico de objetos) incluída para que você veja diretamente o que está errado.

Observe também! Se você possui objetos complexos maiores e apenas deseja comparar partes deles, pode ( use o LINQ para dados de sequência ) criar objetos anônimos para usar com o método acima.

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 });
}
Máx.
fonte
1
Essa é uma excelente maneira de testar, especialmente se você estiver lidando com o JSON (por exemplo, usando um cliente digitado para acessar um serviço da Web). Esta resposta deve ser muito maior.
Roopesh Shenoy 22/03
1
Use Linq! @DmitryBLR (ver o último parágrafo na resposta) :)
Max
3
Esta é uma ótima idéia. Eu usaria o Json.NET mais recente: varpectedJson = Newtonsoft.Json.JsonConvert.SerializeObject (esperado);
BrokeMyLegBiking
2
Isso não funcionará com referências circulares. Use github.com/kbilsted/StatePrinter vez para uma experiência melhorada sobre a abordagem JSON
Carlo V. Dango
2
Isso é verdade @KokaChernov e, às vezes, você deseja falhar no teste, se a ordem não for a mesma, mas se você não quiser falhar se a ordem não for a mesma, poderá fazer uma classificação explícita (usando linq) nas listas antes de passá-las para o método AreEqualByJson. Uma variante simples de "reorganizar" seus objetos antes do teste está no último exemplo de código da resposta. Então isso é muito "universal", eu acho! :)
Max
91

Experimente a biblioteca FluentAssertions:

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

http://www.fluentassertions.com/

Também pode ser instalado usando o NuGet.

dkl
fonte
18
ShouldHave foi descontinuado, portanto, deve ser dto.ShouldBeEquivalentTo (customer); em vez
WhiteKnight
2
Esta é a melhor resposta por esse motivo .
21315 Todd Menier
ShouldBeEquivalent é buggy :(
Konstantin Chernov
3
só tinha o mesmo problema e usado o seguinte, que parece estar funcionando bem:actual.ShouldBeEquivalentTo(expected, x => x.ExcludingMissingMembers())
stt106
1
Esta é uma ótima lib! Não requer a substituição de Igual a Igual e também (se igual for substituída de qualquer maneira, por exemplo, para objetos de valor) não depende da implementação correta. Além disso, a diferença é bem impressa, como Hamcrest faz para Java.
kap
35

Prefiro não substituir Igual apenas para ativar o teste. Não se esqueça de que, se você substituir o Igual, também deverá substituir o GetHashCode ou poderá obter resultados inesperados se estiver usando seus objetos em um dicionário, por exemplo.

Eu gosto da abordagem de reflexão acima, pois ela serve para adicionar propriedades no futuro.

Para uma solução rápida e simples, no entanto, geralmente é mais fácil criar um método auxiliar que testa se os objetos são iguais ou implementar o IEqualityComparer em uma classe que você mantém privada para seus testes. Ao usar a solução IEqualityComparer, você não precisa se preocupar com a implementação do GetHashCode. Por exemplo:

// 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.");
    }
}
Chris Yoxall
fonte
O igual não lida com valores nulos. Eu adicionaria o seguinte antes da sua declaração de retorno no método equals. if (x == nulo && y == nulo) {return true; } if (x == nulo || y == nulo) {return false; } Editei a pergunta para adicionar suporte nulo.
Bobby Cannon
Não estou trabalhando para mim com throw new NotImplementedException (); no GetHashCode. Por que preciso dessa função no IEqualityComparer de qualquer maneira?
love2code 7/03
15

Eu tentei várias abordagens mencionadas aqui. A maioria envolve serializar seus objetos e fazer uma comparação de cadeias. Embora super fácil e geralmente muito eficaz, descobri que fica um pouco curto quando você tem uma falha e algo assim é relatado:

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

Descobrir onde estão as diferenças é uma dor para dizer o mínimo.

Com as comparações de gráficos de objetos do FluentAssertions (ou seja a.ShouldBeEquivalentTo(b)), você recupera isso:

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

Isso é muito melhor. Adquira o FluentAssertions agora, você ficará feliz mais tarde (e, se você aprovar, também promova a resposta do dkl onde o FluentAssertions foi sugerido pela primeira vez).

Todd Menier
fonte
9

Concordo com ChrisYoxall - implementar Equals no seu código principal apenas para fins de teste não é bom.

Se você estiver implementando o Equals porque alguma lógica do aplicativo exige, tudo bem, mas mantenha o código apenas de teste fora do material (também a semântica de verificar o mesmo para teste pode ser diferente do que o aplicativo requer).

Em resumo, mantenha o código somente de teste fora da sua classe.

Uma comparação simples e superficial de propriedades usando reflexão deve ser suficiente para a maioria das classes, embora seja necessário recuar se seus objetos tiverem propriedades complexas. Se seguir as referências, tenha cuidado com referências circulares ou similares.

Sly

Sly Gryphon
fonte
Boa captura de referências circulares. Fácil de superar se você mantiver um dicionário de objetos já na árvore de comparação.
21460 Lucas B
6

As restrições de propriedade , adicionadas no NUnit 2.4.2, permitem uma solução mais legível que a original do OP e produz mensagens de falha muito melhores. Não é de forma alguma genérico, mas se você não precisar fazer isso para muitas classes, é uma solução muito adequada.

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

Não é tão genérico quanto a implementação, Equalsmas fornece uma mensagem de falha muito melhor do que

Assert.AreEqual(ExpectedObject, ActualObject);
Paul Hicks
fonte
4

A solução JSON de Max Wikstrom (acima) faz mais sentido para mim, é curta, limpa e, o mais importante, funciona. Pessoalmente, eu preferiria implementar a conversão JSON como um método separado e colocar a afirmação novamente dentro do teste de unidade como este ...

MÉTODO DE AJUDA:

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

TESTE DE UNIDADE :

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 - Pode ser necessário adicionar uma referência ao System.Web.Extensions na sua solução.

samaspin
fonte
4

Esta é uma discussão bastante antiga, mas eu queria saber se há uma razão pela qual nenhuma resposta proposta NUnit.Framework.Is.EqualToe NUnit.Framework.Is.NotEqualTo?

Tal como:

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

e

Assert.That(LeftObject, Is.Not.EqualTo(RightObject)); 
user2315856
fonte
4
Porque não imprimir os detalhes o que é diferente
Shrage Smilowitz
1

Outra opção é escrever uma restrição personalizada implementando a Constraintclasse abstrata NUnit . Com uma classe auxiliar para fornecer um pouco de açúcar sintático, o código de teste resultante é agradavelmente conciso e legível, por exemplo

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

Para um exemplo extremo, considere a classe que possui membros 'somente leitura', não é IEquatable, e você não pode alterar a classe em teste, mesmo se quiser:

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

O contrato para a Constraintclasse exige que você substitua Matchese WriteDescriptionTo(no caso de uma incompatibilidade, uma narrativa para o valor esperado), mas também substitua WriteActualValueTo(narrativa para o valor real) faça sentido:

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

Além da classe auxiliar:

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

Exemplo de uso:

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

        Assert.That( LeftObject, PortfolioState.Matches( RightObject ) );
    }
}
um dia quando
fonte
1

Gostaria de aproveitar a resposta de @Juanma. No entanto, acredito que isso não deve ser implementado com asserções de teste de unidade. Este é um utilitário que pode muito bem ser usado em algumas circunstâncias por código que não é de teste.

Eu escrevi um artigo sobre o assunto http://timoch.com/blog/2013/06/unit-test-equality-is-not-domain-equality/

Minha proposta é a seguinte:

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

Usando isso com NUnit

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

gera a seguinte mensagem na incompatibilidade.

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
TiMoch
fonte
1

https://github.com/kbilsted/StatePrinter foi escrito especificamente para despejar gráficos de objetos na representação de cadeias com o objetivo de escrever testes de unidade fáceis.

  • Ele vem com os métodos Assert que produzem uma cópia-colar fácil de escapar corretamente para o teste para corrigi-lo.
  • Permite que unittest seja reescrito automaticamente
  • Ele se integra a todas as estruturas de teste de unidade
  • Ao contrário da serialização JSON, as referências circulares são suportadas
  • Você pode filtrar facilmente, para que apenas partes dos tipos sejam descartadas

Dado

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

Você pode, de uma maneira segura, e usando o preenchimento automático do visual studio incluir ou excluir campos.

  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);
Carlo V. Dango
fonte
1

Basta instalar o ExpectedObjects a partir do Nuget, você pode comparar facilmente o valor da propriedade de dois objetos, cada valor da coleção, o valor de dois objetos compostos e o valor parcial da propriedade de comparação por tipo anônimo.

Eu tenho alguns exemplos no github: https://github.com/hatelove/CompareObjectEquals

Aqui estão alguns exemplos que contêm cenários de comparação de objetos:

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

Referência:

  1. ExpectedObjects github
  2. Introdução de ExpectedObjects
In91
fonte
1

Acabei escrevendo uma fábrica de expressões simples:

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

e apenas use-o:

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

É muito útil, pois tenho que comparar a coleção desses objetos. E você pode usar esse comparere em outro lugar :)

Aqui está uma lista com o exemplo: https://gist.github.com/Pzixel/b63fea074864892f9aba8ffde312094f

Alex Zhukovskiy
fonte
0

Desserialize as duas classes e faça uma comparação de cadeias.

EDIT: Funciona perfeitamente, esta é a saída que recebo do 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)

EDIÇÃO DOIS: Os dois objetos podem ser idênticos, mas a ordem em que as propriedades são serializadas não é a mesma. Portanto, o XML é diferente. DOH!

EDIÇÃO TRÊS: Isso funciona. Estou usando nos meus testes. Mas você deve adicionar itens às propriedades da coleção na ordem em que o código em teste os adiciona.

Casey Burns
fonte
1
serializar ? Idéia interessante. Não tenho certeza como ele iria realizar-se em termos de desempenho, embora
Michael Haren
não permitirá que você compare duplos ou decimais com uma determinada precisão.
Noctis
0

Sei que essa é uma pergunta muito antiga, mas o NUnit ainda não tem suporte nativo para isso. No entanto, se você gosta de testes no estilo BDD (ala Jasmine), ficaria agradavelmente surpreendido com o NExpect ( https://github.com/fluffynuts/NExpect , obtenha-o no NuGet), que possui testes de igualdade profunda instalados ali .

(aviso: sou o autor do NExpect)

daf
fonte
-1

Stringify e compare duas strings

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

jmtt89
fonte
-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);
}
Satish Babu
fonte
Obrigado por este snippet de código, que pode fornecer ajuda limitada a curto prazo. Uma explicação adequada melhoraria bastante seu valor a longo prazo, mostrando por que essa é uma boa solução para o problema e a tornaria mais útil para futuros leitores com outras perguntas semelhantes. Por favor edite sua resposta para adicionar alguma explicação, incluindo as suposições que você fez.
Toby Speight
E o que isso acrescenta à resposta de Max ?
Toby Speight