Hamcrest matcher comparing double value from JSON
Asked Answered
V

2

10

I'm using the Hamcrest CoreMatcher classes as part of spring-test integration tests. My JSON looks like:

{"data":[{"distanceInMiles":4,"id":"f97236ba-f4ef-4...

And my integration test looks like:

double miles = 4.0
Activity a = new BasicActivity(miles);
this.activityManager.add(a); // A mock activity manager (in-memory)
...
this.mockMvc.perform(get("/").accept("application/json"))
    .andExpect(jsonPath("$.data[0].distanceInMiles", is(miles)))

However, the assertion fails:

java.lang.AssertionError: JSON path "$.data[0].distanceInMiles"
Expected: is <4.0>
     but: was <4>
    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)

I know that there's a separate IsCloseTo matcher here: http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/number/IsCloseTo.html, but using it like so:

.andExpect(jsonPath("$.data[0].distanceInMiles", closeTo(miles, 0)))

results in a strange error:

java.lang.AssertionError: JSON path "$.data[0].distanceInMiles"
Expected: a numeric value within <0.0> of <4.0>
     but: was a java.lang.Integer (<4>)
    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)

I was hoping to avoid having to include some kind of error - I want the returned value to be exactly 4, I just don't care how many trailing zeroes are included.

Vainglorious answered 5/12, 2015 at 15:51 Comment(3)
The issue is that the double value is used to create the model object that is then checked for as part of the JSON response, so I was hoping to reuse the same variable to avoid typing it twice. I'll update my codeblock to demonstrate.Vainglorious
Are you using Java 8 by the way?Jerk
Yes, I'm using Java 8.Vainglorious
J
5

The problem is that the match is performed on Integers and not on double values.

You are correctly giving a Matcher<Double>. Spring uses Jayway under the hood for parsing the JSON and your JSON path will be evaluated as an Integer object. The matching will fail because an Integer and a Double are always unequal.

As such, you need to change your Matcher to is((int) miles).

If you don't control the JSON you are getting and the distanceInMiles might change, this is more problematic. Jayway will parse "4" as an Integer but it will parse "4.0" as a Double. In this case, you will have to implement your own Matcher that handles both Integer and Double objects by extending TypeSafeMatcher. This would be a simple implementation:

class NumberMatcher extends TypeSafeMatcher<Number> {

    private double value;

    public NumberMatcher(double value) {
        this.value = value;
    }

    @Override
    public void describeTo(Description description) {
        // some description
    }

    @Override
    protected boolean matchesSafely(Number item) {
        return item.doubleValue() == value;
    }

}

It matches any Number by comparing their double value to a known double value.

Jerk answered 5/12, 2015 at 16:25 Comment(2)
Unfortunately closeTo gave me some really weird problems as well - question updated.Vainglorious
@CraigOtis Yes, this is because it is still not the expected type. I forgot that Hamcrest does a type-check. You will have to implement your own matcher.Jerk
M
3

I found out that by default is comparing as float, so try something like:

.body("field_with_double_value",is(100.0f));
Machicolation answered 9/2, 2017 at 17:8 Comment(1)
and how to make such comparison work with double, not float?Pleomorphism

© 2022 - 2024 — McMap. All rights reserved.