DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations

Trending

  • Microservices Decoded: Unraveling the Benefits, Challenges, and Best Practices for APIs
  • VPN Architecture for Internal Networks
  • TDD vs. BDD: Choosing The Suitable Framework
  • Explainable AI: Making the Black Box Transparent
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. How to Verify Equality Without Equals Method

How to Verify Equality Without Equals Method

When testing it is often quite complicated to compare object with complex fields. By using the Unitils library in Java, this all becomes quite simple and elegant.

Lubos Krnac user avatar by
Lubos Krnac
·
Apr. 27, 16 · Tutorial
Like (5)
Save
Tweet
Share
15.08K Views

Join the DZone community and get the full member experience.

Join For Free

I don’t like the equals method, it often requires very ugly code, therefore I try to avoid the need for it as much as possible. But how do you compare objects during testing? This blog post covers just that.

You may argue, that there are various alternatives for generating the equals method or make it more maintainable. For example:

  • Generating the equals method in IDE
  • Using Lombok
  • Using Guava’s or Java 8 helper Objects.equals or Objects.deepEquals

But what about testing it? I met so many developers that were surprised after hearing about unit testing of the equals method. So should we unit test it? YES, of course! If it’s generated by IDE, it often has a lot of null check if statements. Testing such code means covering a lot of test scenarios, besides which the static code analyzer will probably complain about cyclomatic complexity violations.

If you are using objects with custom equals methods in HashMap or HashSet, we need to conform to the hashCode vs equals contract. Eventual issues in this contract can lead to very tricky behavior of our map or set. Therefore I prefer to use some unique String or numeric representation as a key in the map so that I don’t need to create an equals method.  HashSet is a type I try to avoid completely.

So if most common reasons for creating the equals method for production code are avoided, what about testing? Assertions in tests often require comparison of complex objects making sure that their child fields have the same values. Of course it doesn’t make sense to create equals only for testing assertions. Sometimes we also have nested types to compare. And what about comparison of arrays or lists during testing?

Luckily there is a very neat library called Unitils. It provides various testing helpers for Hibernate, Database and I/O testing or mocking. But I was only using a module called Reflection assert. It makes camparison of Java objects a piece of cake during testing.

Example Objects

package net.lkrnac.blog.reflectioncompare;

public class Address {
    private String line1;
    private String line2;
    private String city;
    private String postalCode;

    public Address(String line1, String line2, String city, String postalCode) {
        this.line1 = line1;
        this.line2 = line2;
        this.city = city;
        this.postalCode = postalCode;
    }

    public String getLine1() {
        return line1;
    }

    public String getLine2() {
        return line2;
    }

    public String getCity() {
        return city;
    }

    public String getPostalCode() {
        return postalCode;
    }
}


package net.lkrnac.blog.reflectioncompare;

public class Person {
    private String firstName;
    private String lastName;
    private Address address;

    public Person(String firstName, String lastName, Address address) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.address = address;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public Address getAddress() {
        return address;
    }
}

Notice that Address is nested field in Person class.

Reflection Equals Verification

Now let’s take a look how we would compare instances of above listed types.

package net.lkrnac.blog.reflectioncompare;

import org.junit.Test;

import static org.unitils.reflectionassert.ReflectionAssert.assertReflectionEquals;

public class VerifyObjectsTest {
    @Test
    public void testObjectsSuccess() {
        Address expectedAddress = new Address("Barad-dûr", "Mount Doom", "Mordor", "1");
        Person expectedPerson = new Person("Sauron", null, expectedAddress);

        Address actualAddress = new Address("Barad-dûr", "Mount Doom", "Mordor", "1");
        Person actualPerson = new Person("Sauron", null, actualAddress);

        assertReflectionEquals(expectedPerson, actualPerson);
    }

    @Test
    public void testObjectsFail() {
        Address expectedAddress = new Address("Barad-dûr", null, "Mordor", "1");
        Person expectedPerson = new Person("Sauron", null, expectedAddress);

        Address actualAddress = new Address("Barad-dûr", "Mount Doom", "Mordor", "1");
        Person actualPerson = new Person("Sauron", null, actualAddress);

        assertReflectionEquals(expectedPerson, actualPerson);
    }
}

The first test method created two separate instances of Person and compared them. The second test method intentionally created differences in the Address  instances it created between actual and expected, so that we can explore Unitils output:

junit.framework.AssertionFailedError: 
Expected: Person<firstName="Sauron", lastName=null, address=Address<line1="Barad-dûr", line2=null, city="Mordor", postalCode="1">>
  Actual: Person<firstName="Sauron", lastName=null, address=Address<line1="Barad-dûr", line2="Mount Doom", city="Mordor", postalCode="1">>

--- Found following differences ---
address.line2: expected: null, actual: "Mount Doom"

--- Difference detail tree ---
 expected: Person<firstName="Sauron", lastName=null, address=Address<line1="Barad-dûr", line2=null, city="Mordor", postalCode="1">>
   actual: Person<firstName="Sauron", lastName=null, address=Address<line1="Barad-dûr", line2="Mount Doom", city="Mordor", postalCode="1">>

address expected: Address<line1="Barad-dûr", line2=null, city="Mordor", postalCode="1">
address   actual: Address<line1="Barad-dûr", line2="Mount Doom", city="Mordor", postalCode="1">

address.line2 expected: null
address.line2   actual: "Mount Doom"

The output is very well presented making the nested difference very obvious.

Comparison of Arrays and Collections

Another very useful use case of Unitils Reflections assert module is comparison of arrays and collections:

package net.lkrnac.blog.reflectioncompare;

import org.junit.Test;
import org.unitils.reflectionassert.ReflectionComparatorMode;

import static java.util.Arrays.asList;
import static org.unitils.reflectionassert.ReflectionAssert.assertReflectionEquals;

public class VerifyArraysTest {

    @Test
    public void testCompareArraysSuccess() {
        String [] expectedArray = new String []{"string1", "string2", "string3"};
        String [] actualArray = new String []{"string1", "string3", "string2"};

        assertReflectionEquals(expectedArray, actualArray, ReflectionComparatorMode.LENIENT_ORDER);
    }

    @Test
    public void testCompareArraysFail() {
        String [] expectedArray = new String []{"string1", "string2", "string3"};
        String [] actualArray = new String []{"string1", "string3", "string2"};

        assertReflectionEquals(expectedArray, actualArray);
    }

    @Test
    public void testCompareCollectionsSuccess() {
        String [] expectedArray = new String []{"string1", "string2", "string3"};
        String [] actualArray = new String []{"string1", "string3", "string2"};

        assertReflectionEquals(asList(expectedArray), asList(actualArray), ReflectionComparatorMode.LENIENT_ORDER);
    }

    @Test
    public void testCompareCollectionsFail() {
        String [] expectedArray = new String []{"string1", "string2", "string3"};
        String [] actualArray = new String []{"string1", "string3", "string2"};

        assertReflectionEquals(asList(expectedArray), asList(actualArray));
    }
}

The first test method compares arrays and although the array elements are ordered differently it will succeed, because we instructed Unitils to ignore the order of elements in the array with this parameter ReflectionComparatorMode.LENIENT_ORDER. In the second test method, we don’t use the reflection comparator mode parameter, so order matters. Because it’s is different for actual and expected arrays, test will fail with this output:

junit.framework.AssertionFailedError: 
Expected: ["string1", "string2", "string3"]
  Actual: ["string1", "string3", "string2"]

--- Found following differences ---
[1]: expected: "string2", actual: "string3"
[2]: expected: "string3", actual: "string2"

--- Difference detail tree ---
 expected: ["string1", "string2", "string3"]
   actual: ["string1", "string3", "string2"]

[1] expected: "string2"
[1]   actual: "string3"

[2] expected: "string3"
[2]   actual: "string2"

Again very detailed output about test failure.

Lastly the library also has test methods for comparing lists, so Unitils Reflection assert module can also be used on Java collections.

Example code is hosted on Github.

unit test

Published at DZone with permission of Lubos Krnac, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Trending

  • Microservices Decoded: Unraveling the Benefits, Challenges, and Best Practices for APIs
  • VPN Architecture for Internal Networks
  • TDD vs. BDD: Choosing The Suitable Framework
  • Explainable AI: Making the Black Box Transparent

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: