Digamos que tengo una clase sin método equals (), para la cual no tengo la fuente. Quiero afirmar la igualdad en dos instancias de esa clase.
Puedo hacer varias afirmaciones:
assertEquals(obj1.getFieldA(), obj2.getFieldA());
assertEquals(obj1.getFieldB(), obj2.getFieldB());
assertEquals(obj1.getFieldC(), obj2.getFieldC());
...
No me gusta esta solución porque no obtengo la imagen de igualdad completa si falla una afirmación temprana.
Puedo comparar manualmente por mi cuenta y rastrear el resultado:
String errorStr = "";
if(!obj1.getFieldA().equals(obj2.getFieldA())) {
errorStr += "expected: " + obj1.getFieldA() + ", actual: " + obj2.getFieldA() + "\n";
}
if(!obj1.getFieldB().equals(obj2.getFieldB())) {
errorStr += "expected: " + obj1.getFieldB() + ", actual: " + obj2.getFieldB() + "\n";
}
...
assertEquals("", errorStr);
Esto me da una imagen de igualdad completa, pero es torpe (y ni siquiera he tenido en cuenta los posibles problemas de nulos). Una tercera opción es usar Comparator, pero compareTo () no me dirá qué campos fallaron en la igualdad.
¿Existe una mejor práctica para obtener lo que quiero del objeto, sin subclasificar y anular iguales (ugh)?
java
unit-testing
junit
Ryan Nelson
fuente
fuente
equal
método solo dice si dos instancias son iguales y no nos importa por qué las intancias no son iguales.Object
s tienen unequals
método, probablemente quiso decir que ningún método es igual a anulado.Respuestas:
Mockito ofrece un comparador de reflejos:
Para la última versión de Mockito use:
Para versiones anteriores, use:
fuente
org.mockito.internal.matchers.apachecommons
. Los documentos de Mockito dicen:org.mockito.internal
-> "Clases internas, no para ser utilizadas por clientes". Pondrás tu proyecto en riesgo usando esto. Esto puede cambiar en cualquier versión de Mockito. Lea aquí: site.mockito.org/mockito/docs/current/overview-summary.htmlMockito.refEq()
lugar.Mockito.refEq()
falla cuando los objetos no tienen un conjunto de identificación = (refEq
falla para comparar, ya que el método hashcode no puede comparar los objetos.Aquí hay muchas respuestas correctas, pero también me gustaría agregar mi versión. Esto se basa en Assertj.
ACTUALIZACIÓN: En assertj v3.13.2 este método está en desuso, como señaló Woodz en el comentario. La recomendación actual es
fuente
usingRecursiveComparison()
conisEqualTo()
, de modo que la línea seaassertThat(actualObject).usingRecursiveComparison().isEqualTo(expectedObject);
Generalmente implemento este caso de uso usando org.apache.commons.lang3.builder.EqualsBuilder
fuente
Sé que es un poco viejo, pero espero que ayude.
Me encontré con el mismo problema que tú, así que, después de investigar, encontré pocas preguntas similares a esta y, después de encontrar la solución, respondo lo mismo en esas, ya que pensé que podría ayudar a otros.
La respuesta más votada (no la elegida por el autor) de este pregunta similar , es la solución más adecuada para usted.
Básicamente, consiste en utilizar la biblioteca llamada Unitils .
Este es el uso:
Que pasará incluso si la clase
User
no se implementaequals()
. Puede ver más ejemplos y una afirmación realmente genial llamadaassertLenientEquals
en su tutorial .fuente
Unitils
parece haber muerto, consulte stackoverflow.com/questions/34658067/is-unitils-project-alive .Puedes usar Apache commons lang ReflectionToStringBuilder
Puede especificar los atributos que desea probar uno por uno, o mejor, excluir aquellos que no desea:
Luego compara las dos cadenas de forma normal. En cuanto a que la reflexión es lenta, supongo que esto es solo para probar, por lo que no debería ser tan importante.
fuente
Si está usando hamcrest para sus afirmaciones (assertThat) y no desea extraer bibliotecas de prueba adicionales, puede usar
SamePropertyValuesAs.samePropertyValuesAs
para afirmar elementos que no tienen un método igual anulado.La ventaja es que no tiene que incorporar otro marco de prueba más y dará un error útil cuando la aserción falla (
expected: field=<value> but was field=<something else>
) en lugar deexpected: true but was false
si usa algo comoEqualsBuilder.reflectionEquals()
.La desventaja es que es una comparación superficial y no hay opción para excluir campos (como en EqualsBuilder), por lo que tendrá que trabajar con objetos anidados (por ejemplo, eliminarlos y compararlos de forma independiente).
Mejor caso:
Caso feo:
Entonces, elige tu veneno. Marco adicional (por ejemplo, Unitils), error inútil (por ejemplo, EqualsBuilder) o comparación superficial (hamcrest).
fuente
La biblioteca Hamcrest 1.3 Utility Matchers tiene un matcher especial que utiliza la reflexión en lugar de iguales.
fuente
Algunos de los métodos de comparación de reflexión son superficiales
Otra opción es convertir el objeto a json y comparar las cadenas.
fuente
Con Shazamcrest , puede hacer:
fuente
reflectEquals
devuelvefalse
cuando los objetos tienen referencias diferentes.Comparar campo por campo:
fuente
Las afirmaciones de AssertJ se pueden usar para comparar los valores sin que el
#equals
método se anule correctamente, por ejemplo:fuente
Dado que esta pregunta es antigua, sugeriré otro enfoque moderno utilizando JUnit 5.
Con JUnit 5, hay un método llamado
Assertions.assertAll()
que le permitirá agrupar todas las aserciones en su prueba y ejecutará cada una y generará las aserciones fallidas al final. Esto significa que cualquier afirmación que falle primero no detendrá la ejecución de las últimas afirmaciones.fuente
Puede utilizar la reflexión para "automatizar" la prueba de igualdad total. puede implementar el código de "seguimiento" de igualdad que escribió para un solo campo, luego usar la reflexión para ejecutar esa prueba en todos los campos del objeto.
fuente
Este es un método de comparación genérico, que compara dos objetos de una misma clase por los valores de sus campos (tenga en cuenta que se puede acceder a ellos mediante el método get)
fuente
Me encontré con un caso muy similar.
Quería comparar en una prueba de que un objeto tiene los mismos valores de atributos como la otra, pero los métodos como
is()
,refEq()
, etc, no haría el trabajo por razones como mi objeto que tiene un valor nulo en suid
atributo.Así que esta fue la solución que encontré (bueno, un compañero de trabajo encontró):
Si el valor obtenido de
reflectionCompare
es 0, significa que son iguales. Si es -1 o 1, difieren en algún atributo.fuente
En el caso común con AssertJ, puede crear una estrategia de comparación personalizada:
Usar una estrategia de comparación personalizada en afirmaciones
Ejemplos de AssertJ
fuente
Tenía exactamente el mismo acertijo cuando probaba unitariamente una aplicación de Android, y la solución más fácil que se me ocurrió fue simplemente usar Gson para convertir mis objetos de valor real y esperado
json
y compararlos como cadenas.Las ventajas de esto sobre la comparación manual campo por campo es que usted compara todos sus campos, por lo que incluso si luego agrega un nuevo campo a su clase, se probará automáticamente, en comparación con si estuviera usando un montón de
assertEquals()
de todos los campos, que luego deberían actualizarse si agrega más campos a su clase.jUnit también muestra las cadenas para que pueda ver directamente en qué se diferencian. Sin
Gson
embargo, no estoy seguro de cuán confiable sea el pedido de campo , eso podría ser un problema potencial.fuente
Probé todas las respuestas y nada realmente funcionó para mí.
Así que he creado mi propio método que compara objetos java simples sin profundizar en estructuras anidadas ...
El método devuelve un valor nulo si todos los campos coinciden o la cadena contiene detalles que no coinciden.
Solo se comparan las propiedades que tienen un método getter.
Cómo utilizar
Salida de muestra si hay una discrepancia
La salida muestra los nombres de las propiedades y los valores respectivos de los objetos comparados
Código (Java 8 y superior):
fuente
Como alternativa para junit-only, puede establecer los campos en nulos antes de la aserción igual:
fuente
¿Puede poner el código de comparación que publicó en algún método de utilidad estática?
Entonces puedes usar este método en JUnit así:
assertEquals ("Los objetos no son iguales", "", findDifferences (obj1, obj));
que no es torpe y le brinda información completa sobre las diferencias, si existen (a través de no exactamente en la forma normal de assertEqual, pero obtiene toda la información, por lo que debería ser bueno).
fuente
Esto no ayudará al OP, pero podría ayudar a cualquier desarrollador de C # que termine aquí ...
Como publicó Enrique , debes anular el método de iguales.
Mi sugerencia es no utilizar una subclase. Utilice una clase parcial.
Definiciones de clases parciales (MSDN)
Entonces tu clase se vería así ...
Para Java, estaría de acuerdo con la sugerencia de usar la reflexión. Solo recuerda que debes evitar usar la reflexión siempre que sea posible. Es lento, difícil de depurar e incluso más difícil de mantener en el futuro porque los IDE podrían romper su código al cambiar el nombre de un campo o algo así. ¡Ten cuidado!
fuente
De sus comentarios a otras respuestas, no entiendo lo que quiere.
Solo por el bien de la discusión, digamos que la clase anuló el método de igualdad.
Entonces su UT se verá algo como:
Y ya está. Además, no puede obtener la "imagen de igualdad total" si la afirmación falla.
Por lo que tengo entendido, está diciendo que incluso si el tipo anulara los iguales, no estaría interesado en él, ya que desea obtener la "imagen de igualdad total". Por lo tanto, tampoco tiene sentido extender y anular iguales.
Entonces tiene opciones: o comparar propiedad por propiedad, usando la reflexión o verificaciones codificadas, sugeriría lo último. O: compare representaciones legibles por humanos de estos objetos.
Por ejemplo, puede crear una clase auxiliar que serialice el tipo que desea comparar con un documento XML y luego comparar el XML resultante. en este caso, puede ver visualmente qué es exactamente igual y qué no.
Este enfoque le dará la oportunidad de ver la imagen completa, pero también es relativamente engorroso (y un poco propenso a errores al principio).
fuente
Puede anular el método equals de la clase como:
Bueno, si desea comparar dos objetos de una clase, debe implementar de alguna manera el método equals y el código hash
fuente