Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hamcrest matcher comparing double value from JSON

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.

like image 369
Craig Otis Avatar asked Dec 05 '15 15:12

Craig Otis


2 Answers

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.

like image 151
Tunaki Avatar answered Sep 17 '22 19:09

Tunaki


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

.body("field_with_double_value",is(100.0f));
like image 35
WalterVi Avatar answered Sep 19 '22 19:09

WalterVi