I have a simple pojo class in the format as shown below.

class PojoClass {

    property1;
    ...
    propertyN;

    @override
    public int hashcode() {
        //implementation
    }

    @override
    public boolean equals(Object o) {
        //implementation
    }

    @override
    public String toString)() {
        return "PojoClass[property1= " + property1 ... propertyN + "]";
    }

}

In my test case I do what I need and I have an expected PojoClass object and actual PojoClass Object. In some cases even arrays (expected and actual) of objects of PojoClass. When assert fails, I get message that prints the toString of expected and actual.

toString is not convenient enough in this case as my PojoClass object contains 20 to 30 properties. I am having to manually check all the properties if they match or not. Is there any way in JUnit, to directly specify if which property of expected object did not match that of actual object?

3 Answers

0
Arun Rajagopal On Best Solutions

Solution using Gson. The basic idea behind this solution is to get the objects values from toString and de-serialize it into Json, and then use Maps.difference method in the Gson library to compare two Jsons and figure out which property(s)'s or key(s)'s value is a mismatch.

  1. Update Pojo's to string to return a stringified json of that particular object of POJO
class PojoClass {

    property1;
    ...
    propertyN;

    @override
    public int hashcode() {
        //implementation
    }

    @override
    public boolean equals(Object o) {
        //implementation
    }

    @override
    public String toString)() {
        return new Gson().toJson(this);
    }

}
  1. Test case should look like this, as Hades pointed out.
@Test
public void pojoClassTest() throws Exception {
    List<PojoClass> sourcePojoList = getSourcePojoList();
    List<PojoClass> targetPojoList = getTargetPojoList();
    try {
        Assert.assertArrayEquals(sourcePojoList.toArray(), targetPojoList.toArray());
    }
    catch (ArrayComparisonFailure e) {
        Assert.fail(getMessage(e));
    }
}
  1. getMessage method is where we get the existing message from JUnit, create two Jsons and compare them. Refer this post for the comparison technique.
// The junit error message will be in following format
// java.lang.AssertionError: arrays first differed at element [0]; expected:<{json}> but was:<{json}>
// So we get both source and target Json, and throw in the magic of gson library
// We compare them using method mentioned in link above.
// Now the new message that will be asserted contains message like below (assuming property 1 of the object is the mismatch)
// not equal: value differences={property1=("expected","actual")}

private static String getMessage(AssertionError e) throws IOException {
    String msg = e.getMessage();
    String sourceJson = msg.substring(msg.indexOf("expected:<") + 10, msg.indexOf("> but was:"));
    String targetJson = msg.substring(msg.lastIndexOf("but was:<") + 9, msg.lastIndexOf(">"));
    Gson g = new Gson();
    Type mapType = new TypeToken<Map<String, Object>>(){}.getType();
    Map<String, Object> firstMap = g.fromJson(sourceJson, mapType);
    Map<String, Object> secondMap = g.fromJson(targetJson, mapType);
    return Maps.difference(firstMap, secondMap).toString();
}
1
Hades On

you can try catching ComparisonFailure exception like below
P.S Run one assert at a time i.e. comment another to see which exception is caught

@org.junit.Test
public void someSimpleTest() {
    try {
        Assert.assertEquals("123", "456");//Throw ComparisonFailure 
        Assert.assertArrayEquals(new String[] {"ABC","efg"}, new String[] {"ABCe","xys"});//Throw ArrayComparisonFailure 
    } catch(ComparisonFailure e) {
        System.out.println("Inside block");
        System.out.println(e);
    } catch(ArrayComparisonFailure e) {
        System.out.println("Inside block");
        System.out.println(e);
    }
}

in this example, If I comment the seertEquals and execute assertArrayEquals, It will clearly print the statement

arrays first differed at element [0]; expected:<ABC[]> but was:<ABC[e]>

Hence to answer your question Is there any way in JUnit, to directly specify if which property of expected object did not match that of actual object?

This clearly states the first element are not equal hence it failed.

Similarly, you can check for your toString differences.

http://junit.sourceforge.net/javadoc/org/junit/ComparisonFailure.html

Update For custom object This code will only print the difference between the toString of the Object

class PojoClass {

    String prop1;
    int prop2;
    String prop3;

    public  PojoClass(String prop1, int prop2, String prop3) {
        this.prop1 = prop1;
        this.prop2 = prop2;
        this.prop3 = prop3;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        PojoClass other = (PojoClass) obj;
        if (prop1 == null) {
            if (other.prop1 != null)
                return false;
        } else if (!prop1.equals(other.prop1))
            return false;
        if (prop2 != other.prop2)
            return false;
        if (prop3 == null) {
            if (other.prop3 != null)
                return false;
        } else if (!prop3.equals(other.prop3))
            return false;
        return true;
    }
    @Override
    public String toString() {
        return "PojoClass [prop1=" + prop1 + ", prop2=" + prop2 + ", prop3=" + prop3 + "]";
    }

Junit to print the assertion error difference

@org.junit.Test
    public void someSimpleTest() {
        try {   
            PojoClass class1 = new PojoClass("Sample", 1, "foo");
            PojoClass class2 = new PojoClass("Sample1", 1, "foo2");
            Assert.assertEquals(class1, class2);
        } catch(AssertionError e) {
            getStringDiff(e);
        }
    }

    private void getStringDiff(AssertionError e) {
        String msg = e.getMessage();
        String str1 = msg.substring(msg.indexOf("<"), msg.indexOf(">")+1);
        String str2 = msg.substring(msg.lastIndexOf("<"), msg.lastIndexOf(">")+1);
        System.out.println(StringUtils.difference(str1, str2));
    }
1
Mansoor On

I believe there's no such feature in jUnit.

The assertEquals method will ultimately return expected.equals(actual), maybe you can modify your implementation of equals method in a way that it logs which properties if any are mismatching causing the equals method to return false.