2

I have some objects (of different class) say PersonType1 and PersonType2 and trying to compare the fields are equal in the test case. The scenario is like:

jsonA -> PersonTypeA;
jsonB -> PersonTypeB;

both PersonTypeA.class and PersonTypeB.class have the same properties, say id, name, etc.

I am trying to assert the values are equal like this:

assertEquals(personA.getId(), personB.getId());

I am not able to use standard reflection equals provided by Mockito as the classes are not same. I am not planning a write a bunch of extra code to compare the obejcts. Something more in the line of:

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

to

Assert.assertTrue(compareProperties(expected, excludeFields).matches(actual));

Edit 1: This is not a duplicate question I am trying to compare 2 different objects of different classes if they have the same value in properties which have the same name. I can write the code but looking for some existing util methods if already present in junit5, mockito, hamcrest, etc

assertEquals(objA.getId(), objB.getId());
//like this there are 30 properties

also there are nested objects like list, set of Strings. Comparing them manually is too much pain

Edit 2: Maybe this will explain better

I do not have control on the POJOs. They are owned by someone else. So, essentially if I have 2 classes

class Cat{
  String id;
  String name;
  // 30 more properties
}

class Dog{
  String id;
  String name;
 // 30 more properties
}

How to compare cat and dog have same values in id, name, etc. Because there are so many properties I do not want to write

assertEquals(cat.getId(), dog.getId());

So, is there any utility to do that? We can do the other way round, if we have 2 objects (of different classes) we can copy the properties using BeanUtils.copyProperies(o1, o2) in Spring and apache bean utils. Similarly is there a way to compare the properties?

Subhomoy Sikdar
  • 526
  • 5
  • 19
  • So what is your question? – cmoetzing Jun 25 '19 at 06:12
  • so, what is stopping you from creating a method compareProperties that allows just that? – Stultuske Jun 25 '19 at 06:12
  • 2
    Pretty sure it's not a duplicate of that, @Vimukthi_R – Docteur Jun 25 '19 at 06:18
  • @Stultuske i do not want to write custom code.. looking for some existing util method – Subhomoy Sikdar Jun 25 '19 at 07:00
  • so, you want some already existing code that is custom made to compare your personal classes? – Stultuske Jun 25 '19 at 07:01
  • @Stultuske the classes does not matter, just compare the values are same for same properties.. same as ReflectionEquals but instead of objects of same class, here the classes are different – Subhomoy Sikdar Jun 25 '19 at 07:03
  • @Vimukthi_R this is not a duplicate question – Subhomoy Sikdar Jun 25 '19 at 07:05
  • @SubhomoySikdar "the classes do not matter". try calling myObject1.equals(myObject2) on your code, and you'll see they matter very much. – Stultuske Jun 25 '19 at 07:17
  • @Stultuske I am NOT calling equals or comparing the objects as whole.. The way ReflectionEquals works is it looks into the class for accessors and then checks the value in the objects are same for each accessor or not.. in my case there will be 2 classes and only the common accessors needs to be picked.. I am trying to compare only the common properties.. the objects are never equal – Subhomoy Sikdar Jun 25 '19 at 08:48
  • yes, but you do, however, assume that someone out there knows how your objects are constructed, which variable of type a is mapped to which one of type b, ... – Stultuske Jun 25 '19 at 09:07
  • @Stultuske true.. but the copyProperties (spring or apache bean utils) works in the same way.. if 2 objects (different class altogether) have same property with same type, it copies the values.. I was looking for the same thing other way round, if possible to compare the same – Subhomoy Sikdar Jun 25 '19 at 09:14

2 Answers2

2

There are many ways to achieve the goal, to name a few:

Option 1

Create your own Matcher and you'll be able to use something like this:

assertThat(actual, MyMatchers.semanticallyEqualsTo(expected));

Option 2

Create a custom method "compareProperties(expected, actual)" less BDD style but still can work

Option 3

Provide a converter method:

class PersonTypeA {
   public PersonTypeB asTypeB() {
     ... if it has same properties, the conversion is trivial
     return new PersonTypeB   
   }
}

and then use this method in test so that expected and actual will be of the same type and you can use a regular assertEquals

Option 4

Provide some type of "common representation for both objects". You've mentioned that they're somehow related to JSON, so you can probably present both values as some kind of JSON:

 class PersonTypeA {
    public JSONObject toJson() {...}
 }

 class PersonTypeB {
    public JSONObject toJson();
 }

Then in test you can compare JSON to JSON. Since its more complicated than a simple string comparison (JSON's pairs of key/value are unordered) you might need a JSON comparison library for tests. Something like jsonassert, there are others as well.

Notes: Options 3 and 4 can also be implemented "externally" to PersonTypeA and PersonTypeB in some utility class.

Personally I would go with the first option, especially if you'll see that you face this issue in many tests because after all it allows to write the most clean test and also slightly better performance wise at least than option 4, no need to fiddle with JSON conversions.

But technically all the options are valid here

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • 1
    I am not planning to write a custom matcher or implement compareProperties.. as for option 3 and 4, it is not a good idea to convert to json and compare as the json conversion does not ensure the properties will appear in the same sequence – Subhomoy Sikdar Jun 25 '19 at 07:08
  • As for the matcher, why not actually? its one class implementing some interface, nothing really complicated. But as you wish of course, your project, your tests :) I'm just stating options. As for the order of properties, that's why I've said that its not just a string comparison, so you'll need a Json comparison library. Its only for option 4. Option 3 doesn't assume any JSONs... – Mark Bramnik Jun 25 '19 at 07:14
  • Hi Mark.. sorry actually for option 3: I dont own the POJOs so I cannot modify the them to add a converter method.. what I was looking for is like ReflectionEquals which takes the class and then reflectively checks the accessors and compares the values between the objects; here in my case there will be 2 classes and the comparison should happen only between common accessors.. The reason I dont want to write the code is, the classes have List, Set, Map as fields and the comparison should include them as well.. and then I will end up writing a lot of code – Subhomoy Sikdar Jun 25 '19 at 08:46
1

I did not get any solution but will write out what I did.

For same class object comparison, we can use Mockito ReflectionEquals like below. Follow the link for the original answer

assertTrue(new ReflectionEquals(deployments.get(0)).matches(returnedDeployments.get(0)));

https://stackoverflow.com/a/29793460/3497657

I saw because ReflectionEquals is an internal class it is better to use Hamcrest samePropertyValuesAs like this:

assertThat(returnedDeployments.get(0), samePropertyValuesAs(deployments.get(0)));

For objects of different class however I did not find anything so I had to write a Matcher myself

public class HasSameProperties<T> extends TypeSafeDiagnosingMatcher<T> {
  private ClassA obj1;

  public HasSameProperties(ClassA obj1) {
    this.obj1 = obj1;
  }

  @Override
  protected boolean matchesSafely(T item, Description mismatchDescription) {
     if (item instanceof ClassA)
       return matchClassA((ClassA) item);

     return null;
  }

  public static Matcher<ClassB> hasSameProperties(ClassA objA) {
    return new HasSameProperties(objA);
  }

  private boolean matchClassA(ClassB objB) {
    assertThat(objB.getId(), equalTo(objA.getId()));
    //other getters and setters
    return true;
  }

Subhomoy Sikdar
  • 526
  • 5
  • 19