How to state equality on two classes without equals method?

Say I have a class without the equals () method for which there is no source. I want to claim equality in two instances of this class.

I can make several statements:

assertEquals(obj1.getFieldA(), obj2.getFieldA()); assertEquals(obj1.getFieldB(), obj2.getFieldB()); assertEquals(obj1.getFieldC(), obj2.getFieldC()); ... 

I do not like this decision because I do not get the full picture of equality if the early statement fails.

I can manually compare myself and track the result:

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

This gives me a complete picture of equality, but clumsy (and I didn’t even consider possible zero issues). The third option is to use Comparator, but compareTo () will not tell me which fields did not get the equality.

Is there a best practice for getting what I want from an object, without subclassing and overriding peers (ugh)?

+80
java unit-testing junit
Aug 27 '12 at 18:15
source share
20 answers

Mockito offers reflection mapping:

For the latest version of Mockito, use:

 Assert.assertTrue(new ReflectionEquals(expected, excludeFields).matches(actual)); 

For older versions, use:

 Assert.assertThat(actual, new ReflectionEquals(expected, excludeFields)); 
+47
Apr 22 '15 at 9:37
source share

I usually implement this usecase using org.apache.commons.lang3.builder.EqualsBuilder

 Assert.assertTrue(EqualsBuilder.reflectionEquals(expected,actual)); 
+39
03 Mar. '14 at 23:25
source share

There are many correct answers here, but I would like to add my version as well. This is based on Assertj.

 import static org.assertj.core.api.Assertions.assertThat; public class TestClass { public void test() { // do the actual test assertThat(actualObject) .isEqualToComparingFieldByFieldRecursively(expectedObject); } } 
+33
Jun 27 '17 at 13:41 on
source share

I know this is a little old, but I hope this helps.

I encountered the same problem as you, so after the investigation I found several similar questions than this one, and finding a solution, I answer the same ones, because I thought that it could help others.

The answer to this similar question (not the one chosen by the author) is the most suitable solution for you.

Essentially, it consists of using a library called Unitils .

This usage:

 User user1 = new User(1, "John", "Doe"); User user2 = new User(1, "John", "Doe"); assertReflectionEquals(user1, user2); 

Which will pass even if the User class does not implement equals() . You can see more examples and really assertLenientEquals statements in their guide .

+11
Dec 17 '14 at 17:06
source share

You can use Apache Commons Lang ReflectionToStringBuilder

You can specify the attributes that you want to check one at a time, or, even better, exclude those that you do not need:

 String s = new ReflectionToStringBuilder(o, ToStringStyle.SHORT_PREFIX_STYLE) .setExcludeFieldNames(new String[] { "foo", "bar" }).toString() 

Then you compare the two lines as usual. As for slow reflection, I assume this is for testing only, and therefore should not be so important.

+7
Aug 28 2018-12-12T00:
source share

If you use hamcrest for your statements (assertThat) and don’t want to use additional test libraries, then you can use SamePropertyValuesAs.samePropertyValuesAs to claim elements that do not have an overridden equals method.

On the plus side, you don’t need to embed another test environment, and it will give a useful error when confirmation fails ( expected: field=<value> but was field=<something else> ) instead of expected: true but was false if you use something like EqualsBuilder.reflectionEquals() .

The disadvantage is that this is a small comparison and there is no way to exclude fields (as in EqualsBuilder), so you have to work with nested objects (for example, delete them and compare them independently).

Best case:

 import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs; ... assertThat(actual, is(samePropertyValuesAs(expected))); 

Bad case:

 import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs; ... SomeClass expected = buildExpected(); SomeClass actual = sut.doSomething(); assertThat(actual.getSubObject(), is(samePropertyValuesAs(expected.getSubObject()))); expected.setSubObject(null); actual.setSubObject(null); assertThat(actual, is(samePropertyValuesAs(expected))); 

So, choose your poison. Extra structure (e.g. Unitils), useless error (e.g. EqualsBuilder), or superficial comparison (hamstring).

+6
Mar 01 '18 at 3:52
source share

The Hamcrest 1.3 Utility Matchers library has special matches that use reflection instead of peers.

 assertThat(obj1, reflectEquals(obj2)); 
+3
Jan 07 '15 at 10:48
source share

Using Shazamcrest , you can do:

 assertThat(obj1, sameBeanAs(obj2)); 
+2
Sep 12 '18 at 17:36
source share

Comparing fields by field:

 assertNotNull("Object 1 is null", obj1); assertNotNull("Object 2 is null", obj2); assertEquals("Field A differs", obj1.getFieldA(), obj2.getFieldA()); assertEquals("Field B differs", obj1.getFieldB(), obj2.getFieldB()); ... assertEquals("Objects are not equal.", obj1, obj2); 
0
Aug 27 '12 at 18:23
source share

You can use reflection to “automate” full equality testing. you can implement the equality tracking code that you wrote for one field, and then use reflection to run this test in all fields of the object.

0
Aug 27 '12 at 18:50
source share

This is a general comparison method that compares two objects of the same class with its field values ​​(keep in mind that they are accessible by the get method)

 public static <T> void compare(T a, T b) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { AssertionError error = null; Class A = a.getClass(); Class B = a.getClass(); for (Method mA : A.getDeclaredMethods()) { if (mA.getName().startsWith("get")) { Method mB = B.getMethod(mA.getName(),null ); try { Assert.assertEquals("Not Matched = ",mA.invoke(a),mB.invoke(b)); }catch (AssertionError e){ if(error==null){ error = new AssertionError(e); } else { error.addSuppressed(e); } } } } if(error!=null){ throw error ; } } 
0
Feb 01 '17 at 19:19
source share

I came across a very similar case.

I wanted to compare in the test that the object has the same attribute values ​​as the others, but methods such as is() , refEq() , etc., will not work for reasons such as my object having a null value in your id .

So, this was the solution I found (well, found a colleague):

 import static org.apache.commons.lang.builder.CompareToBuilder.reflectionCompare; assertThat(reflectionCompare(expectedObject, actualObject, new String[]{"fields","to","be","excluded"}), is(0)); 

If the value obtained from reflectionCompare is 0, then they are equal. If it is -1 or 1, they differ in some attribute.

0
May 19 '17 at 16:45
source share

In general, with AssertJ you can create your own comparison strategy:

 assertThat(frodo).usingComparator(raceComparator).isEqualTo(sam) assertThat(fellowshipOfTheRing).usingElementComparator(raceComparator).contains(sauron); 

Using a special comparison strategy in statements

AssertJ Examples

0
Oct 02 '17 at 21:39 on
source share

Some methods for comparing reflections are small

Another option is to convert the object to json and compare the strings.

 import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; public static String getJsonString(Object obj) { try { ObjectMapper objectMapper = new ObjectMapper(); return bjectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); } catch (JsonProcessingException e) { LOGGER.error("Error parsing log entry", e); return null; } } ... assertEquals(getJsonString(MyexpectedObject), getJsonString(MyActualObject)) 
0
Mar 26 '18 at 21:31
source share

I had the same riddle when I tested an Android application, and the simplest solution I came up with was just to use Gson to convert my real and expected value objects to json and compare them as strings.

 String actual = new Gson().toJson( myObj.getValues() ); String expected = new Gson().toJson( new MyValues(true,1) ); assertEquals(expected, actual); 

The advantage of doing this manually compared by field is that you compare all your fields, so even if you add a new field to your class later, it will be automatically checked, compared to using a bunch of assertEquals() for all fields, which then need to be updated if you add more fields to your class.

jUnit also displays strings for you so you can directly see where they differ. Not sure how reliable Gson field Gson , this can be a potential problem.

0
Apr 07 '18 at 9:29
source share

I tried all the answers and nothing worked for me.

So, I created my own method that compares simple Java objects without delving into nested structures ...

The method returns zero if all fields match or the string contains mismatch details.

Only properties that have a retrieval method are compared.

How to use

  assertNull(TestUtils.diff(obj1,obj2,ignore_field1, ignore_field2)); 

Example output if there is a mismatch

The output shows the property names and the corresponding values ​​of the compared objects.

 alert_id(1:2), city(Moscow:London) 

Code (Java 8 and later):

  public static String diff(Object x1, Object x2, String ... ignored) throws Exception{ final StringBuilder response = new StringBuilder(); for (Method m:Arrays.stream(x1.getClass().getMethods()).filter(m->m.getName().startsWith("get") && m.getParameterCount()==0).collect(toList())){ final String field = m.getName().substring(3).toLowerCase(); if (Arrays.stream(ignored).map(x->x.toLowerCase()).noneMatch(ignoredField->ignoredField.equals(field))){ Object v1 = m.invoke(x1); Object v2 = m.invoke(x2); if ( (v1!=null && !v1.equals(v2)) || (v2!=null && !v2.equals(v1))){ response.append(field).append("(").append(v1).append(":").append(v2).append(")").append(", "); } } } return response.length()==0?null:response.substring(0,response.length()-2); } 
0
Feb 01 '19 at 16:58
source share

Can you put the comparison code you posted into some static utility method?

 public static String findDifference(Type obj1, Type obj2) { String difference = ""; if (obj1.getFieldA() == null && obj2.getFieldA() != null || !obj1.getFieldA().equals(obj2.getFieldA())) { difference += "Difference at field A:" + "obj1 - " + obj1.getFieldA() + ", obj2 - " + obj2.getFieldA(); } if (obj1.getFieldB() == null && obj2.getFieldB() != null || !obj1.getFieldB().equals(obj2.getFieldB())) { difference += "Difference at field B:" + "obj1 - " + obj1.getFieldB() + ", obj2 - " + obj2.getFieldB(); // (...) } return difference; } 

How can you use this method in JUnit as follows:

assertEquals ("Objects are not equal", "", findDifferences (obj1, obj));

which is not awkward and gives you full information about the differences, if they exist (not quite in the normal form of assertEqual, but you get all the information to be good).

-one
Aug 27 '12 at 18:33
source share

This will not help the OP, but it can help any C # developers that end here ...

As Enrique posted , you must override the equals method.

Is there a best practice for getting what I want from an object, without subclassing and overriding peers (ugh)?

My suggestion is to not use a subclass. Use a partial class.

Incomplete Class Definitions (MSDN)

So your class will look like ...

 public partial class TheClass { public override bool Equals(Object obj) { // your implementation here } } 

For Java, I agree with the suggestion to use reflection. Just remember that you should avoid using reflection whenever possible. It is slow, difficult to debug, and even harder to maintain in the future because IDEs can break your code by renaming a field or something like that. Be careful!

-one
Aug 27 '12 at 19:04
source share

From your comments on the other answers, I do not understand what you want.

Just for discussion, let's say the class overrides the equals method.

So your UT will look something like this:

 SomeType expected = // bla SomeType actual = // bli Assert.assertEquals(expected, actual). 

And you're done. Moreover, you cannot get a “full picture of equality” if the statement is not fulfilled.

From what I understand, you say that even if the type made the redefinition equal, you would not be interested in this because you want to get a "picture of complete equality". Thus, there is no point in spreading and redefining peers.

So, you have options: either compare the property by property, using reflection or hard-coded checks, I would suggest the latter. Or: compare human readable representations of these objects.

For example, you can create a helper class that serializes the type you want to map to an XML document and compare the resulting XML! in this case, you can visually see what exactly is and what is not.

This approach will give you the opportunity to take a look at the big picture, but it is also relatively cumbersome (and a small mistake is prone at first).

-one
Aug 27 '12 at 19:28
source share

You can override the class equals method, for example:

 @Override public int hashCode() { int hash = 0; hash += (app != null ? app.hashCode() : 0); return hash; } @Override public boolean equals(Object object) { HubRule other = (HubRule) object; if (this.app.equals(other.app)) { boolean operatorHubList = false; if (other.operator != null ? this.operator != null ? this.operator .equals(other.operator) : false : true) { operatorHubList = true; } if (operatorHubList) { return true; } else { return false; } } else { return false; } } 

Well, if you want to compare two objects with a class, you must somehow implement the equals and hash code methods

-3
Aug 27 '12 at 18:20
source share



All Articles