Tag Archives: Test Assertion

How to verify equality without equals method

twittergoogle_plusrss

Facebooktwittergoogle_plusredditlinkedinmail

I don’t like equals method. It often requires very ugly code. Therefore I try to avoid need for it as much as possible. But how to compare objects during testing? This blog post will cover it.

You may argue, that there are various possibilities to generate equals method or make it more maintainable. For example:

But what about testing it? I met so many developers that were surprised after hearing about unit testing of 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. Also static code analyzer will probably complain about cyclomatic complexity violations.

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

So if most common reasons for creating equals method for production code are avoided, what about testing? Verifications in tests often require to compare if objects have fields with 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 very neat library called Unitils. It provides various testing helpers for Hibernate, Database and I/O testing or mocking. But I was using only module called Reflection assert. It makes comparison of Java objects during testing piece of cake.

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

First test method created two separate instances of Person and compares them. Second test method intentionally creates difference in nested Address field 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"

Very nice output 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));
    }
}

First test method is comparing arrays. It will succeed, because we instructed Unitils to ignore order of elements with parameter ReflectionComparatorMode.LENIENT_ORDER. In second test method, we don’t use 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 there are also included test methods comparing lists. So Unitils Reflection assert module can be easily used also for Java collections.

Example code is hosted on Github.

twittergoogle_plusrss

Verifying DateTime and Date with Hamcrest

twittergoogle_plusrss

Facebooktwittergoogle_plusredditlinkedinmail

Since I started diving into automated testing and practicing TDD, verification of date values was pain. Luckily there is nice library for legacy Date and new Java 8 DateTime APIs, which cures this pain.

If you belong to healthier part of Java development community and practicing unit testing on daily basis, you probably are aware of Hamcrest Java library. It can make your tests much more readable. It’s architecture is very modular and is used by various other testing libraries.

Major part of it’s flexibility is it concept of Matcher. I am not going to dive into this concept now. If you are not familiar, just take a quick look at Hamcrest tutorial. One of the matcher you can plug into your testing toolbox is library hamcrest-date. With this library we can easily test that date was generated within certain range:

    @Test
    public void validateDate() {
        //GIVEN
        Date expectedDate = new Date();

        //WHEN
        Date actualDate = new Date();

        //THEN
        assertThat(actualDate, DateMatchers.within(2, ChronoUnit.SECONDS, expectedDate));
    }

We can do that also for Java 8 types:

    @Test
    public void validateDateTime() {
        //GIVEN
        LocalDateTime expectedDateTime = LocalDateTime.now();

        //WHEN
        LocalDateTime actualDateTime = LocalDateTime.now();

        //THEN
        assertThat(actualDateTime, LocalDateTimeMatchers.within(2, ChronoUnit.SECONDS, expectedDateTime));
    }

Or pick various exotic verifications hamcrest-core library provides:

    @Test
    public void validateZonedDateTime() {
        //GIVEN
        ZonedDateTime expectedDateTime = ZonedDateTime.of(2016, 3, 20, 13, 3, 0, 0, ZoneId.of("GMT+1"));

        //WHEN
        ZonedDateTime actualDateTime = ZonedDateTime.of(2016, 3, 20, 13, 3, 0, 0, ZoneId.of("GMT-0"));

        //THEN
        assertThat(actualDateTime, ZonedDateTimeMatchers.sameDay(expectedDateTime));
        assertThat(actualDateTime, ZonedDateTimeMatchers.after(expectedDateTime));
        assertThat(actualDateTime, ZonedDateTimeMatchers.isSunday());
        assertThat(actualDateTime, ZonedDateTimeMatchers.isMarch());
    }

Kudos to creator for this nice little library. This example is hosted in Github.

twittergoogle_plusrss