1

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?

subject-q
  • 91
  • 3
  • 19

3 Answers3

1

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));
    }
Amit Kumar Lal
  • 5,537
  • 3
  • 19
  • 37
  • I will try using this for `assertEquals`, but when `assertArrayEquals` fails, it does not throw `ComparisonFailure` – subject-q May 21 '19 at 09:46
  • when the assertion fails in comparison then yes it will. Please try and let me know if it works for you. – Amit Kumar Lal May 21 '19 at 10:16
  • When assertion fails in `assertEquals` it does, not in `assertArrayEquals` – subject-q May 21 '19 at 10:30
  • @ArunRajagopal check the new update works for both. Please upvote and accept if that's what you want. – Amit Kumar Lal May 21 '19 at 11:04
  • I am getting exception, but there is no info in that, I can use. Only the full objects (expected and actual) are present. Not the specific property – subject-q May 21 '19 at 11:18
  • 1
    I have updated again with explanation, please have a look., You can do some slice and Dice on the message. Moreover, The original question was about getting the property which didn't match. – Amit Kumar Lal May 21 '19 at 12:23
  • Almost. I got an idea from your answer. I will write how I solved it and will mark as answer. Thanks for your support – subject-q May 28 '19 at 10:22
1

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.

Mansoor Ali
  • 153
  • 12
  • You are correct. There is no such inbuilt feature in JUnit as far as I researched. Modifying equals would have been sufficient. But in my case, I have hundreds of Pojos. Every Pojos have a straightforward equals which I do not want to disturb. I will write how I solved this and mark as answer. – subject-q May 28 '19 at 10:24
0

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();
}
subject-q
  • 91
  • 3
  • 19