47

I have two objects A and B. Object A is actual result I get during JUnit test. Object B is I do a actual end db call and assume as expected review. I need to assert that two object instances A and B are equal in value and not actual object.

We have a method called assertEquals(obj expected, obj actual) in JUnit but I don't want that way.

Is there a way or workaround to achieve the same?

acdcjunior
  • 132,397
  • 37
  • 331
  • 304
thirstyladagain
  • 571
  • 1
  • 7
  • 10
  • For checking that two objects have the same values I use the following: **(success)** `assertThat(new Question("title", "id"), is(new Question("title", "id")));` **(fail)** `assertThat(new Question("title1", "id1"), is(new Question("title", "id")));` **With the following imports** `import static org.hamcrest.MatcherAssert.assertThat;` `import static org.hamcrest.CoreMatchers.is;` – Francois Jul 26 '19 at 07:40

5 Answers5

64

Think about exactly what it is you're trying to achieve. Do you want to test object identity or verify that the two objects have exactly the same data? From your suggestion of assertEquals I am guessing you want to go field-by-field.

If the objects are shallow you can use

assertThat(actual, samePropertyValuesAs(expected));

This fails when the object is a composite though. If you want to walk the entire graph you can use the sameBeanAs matcher we wrote some time back to address this issue:

assertThat(actual, sameBeanAs(expected));

If you're using AssertJ then you can also use the built-in functionality like this:

assertThat(actual).isEqualToComparingFieldByField(expected);

Updated in 06/21 - apparently this is deprecated and has been replaced with:

assertThat(actual).usingRecursiveComparison().isEqualTo(expected)

One thing you don't want to do it override the equals method for this as that might change in the future to accommodate business need.

tddmonkey
  • 20,798
  • 10
  • 58
  • 67
  • 2
    Do you know any Hamcrest matcher which we can use? – sol4me Dec 22 '14 at 16:23
  • 1
    samePropertyValuesAs is a built-in Hamcrest matcher. sameBeanAs is a custom Hamcrest matcher we wrote at Shazam to solve this exact issue – tddmonkey Dec 22 '14 at 16:26
  • 7
    Use assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected) if you have object within object. – visrahane Dec 10 '16 at 15:15
  • Only shazamcrest to-json convertion and comparison helped to compare POJOs with nested objects: `assertThat(actual, sameBeanAs(expected));` – Zon Jan 30 '19 at 14:24
  • Can i use objectMapper() to convert my Objects in json and compare? Is this a right way. – Adib Rajiwate Feb 08 '19 at 06:32
  • I don't work for shazam anymore, but from what I remember there was no facility to provide a configurable json mapper. You could always just do it by hand to get the same effect – tddmonkey Feb 08 '19 at 16:42
  • 1
    since `isEqualToComparingFieldByField` is now deprecated, see `usingRecursiveComparison`, example: `assertThat(actual).usingRecursiveComparison().isEqualTo(expected);` – DependencyHell Jun 10 '21 at 13:35
20

If you want deep equality, you can use Commons Lang3 EqualsBuilder#reflectionEquals():

Assert.assertTrue(EqualsBuilder.reflectionEquals(expected,actual));
acdcjunior
  • 132,397
  • 37
  • 331
  • 304
  • This requires you implement equals and hashcode on all levels, which make it not that useful. What is more useful is to perform a deep comparison until the primitive data types. – John Zhang Aug 10 '18 at 12:44
  • Doesn't work for nested objects, though they have the same fields and values, but different instances. – Zon Jan 30 '19 at 14:01
9

using assertj, you can assert each members of those 2 objects!

e.g. assertThat(actual).isEqualToComparingFieldByField(expected);

aurelius
  • 3,946
  • 7
  • 40
  • 73
0

assertEquals() calls equals() on your objects, and there is no way around that. What you can do is to implement something like public boolean like(MyClass b) in your class, in which you would compare whatever you want. Then, you could check the result using assertTrue(a.like(b)).

Predrag Maric
  • 23,938
  • 5
  • 52
  • 68
  • 1
    This means polluting your production code with a method purely for test. Also using assertTrue will give you absolutely no diagnostics on /why/ the test has failed – tddmonkey Dec 22 '14 at 15:42
  • I agree on polluting production code with test code, but not on diagnostics why the test failed. If it failed, it failed because actual objects is not "like" expected. If you want more precise info, add a series of asserts for every relevant field . – Predrag Maric Dec 22 '14 at 15:45
  • The diagnostics you get from this is something like "expected true: got false". If you modify the test to make better use of assertions you don't need to do any other work. If you see my answer, all suggestions will produce quality diagnostics not only telling that the test failed but also why – tddmonkey Dec 22 '14 at 15:47
  • I wasn't familiar with junit matchers, I'll look into them, sounds like just the thing for this problem. – Predrag Maric Dec 22 '14 at 15:57
  • Your JUnit world is about to get a whole lot bigger :) – tddmonkey Dec 22 '14 at 16:00
0

You will need to override the equals and for good practise hashCode method for your Class. After that assertEquals(obj expected, obj actual) will not check for reference but will use the logic that you have implemented inequals method.

sol4me
  • 15,233
  • 5
  • 34
  • 34
  • 5
    I consider this very bad practise. You're overriding equals purely to satisfy a test which needs field-by-field comparison. If the equals contract changes later on then your test will still pass but is not fundamentally broken – tddmonkey Dec 22 '14 at 15:43
  • 1
    @MrWiggles If we have 2 instance of Some class X then isn't it better to have a decent implementation of `equal` so whenever we need to compare those object we can rely on equals implementation rather then default `equals` implementation inherited from Object class – sol4me Dec 22 '14 at 15:56
  • 1
    If your production code needs the equals then yes, for example if you're putting it inside a Set. The problem with overriding for a field check like in this instance is that you do a default equals implementation checking each and every field. If at a later point the equals method should change for a genuine business reason, e.g. compare only on ID, the test that originally needed it is now broken but it won't fail. Also, using equals for this test will give you no diagnostic information about why the objects are different, just that they are – tddmonkey Dec 22 '14 at 15:59
  • @MrWiggles I think in that situation we would be better of using matchers – sol4me Dec 22 '14 at 16:17
  • I don't get why this is bad practice. The equals() method must properly identify two objects as equals. That is going to be the exact same conditions in the "real" application and the unit test. If you need very specialized comparison strategies in your application you're probably doing it very wrong by changing equals() and rather should be using Comparators. – Gimby Dec 22 '14 at 16:25
  • @Gimby that was my initial thought when Iposted the original solution but may be there are some specialized matchers that allow to achieve the same and if so , then we don't have to change anything in business class. I asked MrWiggles to post some Hamcrest matcher which achieve the same – sol4me Dec 22 '14 at 16:28