3

I would like to implement a test that checks that my filter for a floating point variable works well. I send the filter and expect the collection to have values only below the defined value.

    final Double orderPrice = 0.0;

    this.mockMvc.perform(post(SEARCH_ROUTE)
        .contentType(contentType)
        .content(json(buildSearchDtoOnFilter(
            new FilterDto() {{
                setFieldName("orderPrice");
                setFilterType(FilterType.LESS);
                setFilterValue(orderPrice.toString());
            }}))))
        .andExpect(status().isOk())
        .andExpect(content().contentType(contentType))

        .andExpect(jsonPath("$.orders", not(empty())))
        .andExpect(jsonPath("$.orders.[*].orderPrice", everyItem(lessThan(orderPrice))));

When executing last line of the checks eventually I get the exception

java.lang.ClassCastException: java.lang.Double cannot be cast to java.math.BigDecimal

at java.math.BigDecimal.compareTo(BigDecimal.java:220)
at org.hamcrest.number.OrderingComparison.matchesSafely(OrderingComparison.java:33)
at org.hamcrest.number.OrderingComparison.matchesSafely(OrderingComparison.java:12)
at org.hamcrest.TypeSafeMatcher.matches(TypeSafeMatcher.java:65)
at org.hamcrest.core.Every.matchesSafely(Every.java:18)
at org.hamcrest.core.Every.matchesSafely(Every.java:8)
at org.hamcrest.TypeSafeDiagnosingMatcher.matches(TypeSafeDiagnosingMatcher.java:55)
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:12)
at org.springframework.test.util.JsonPathExpectationsHelper.assertValue(JsonPathExpectationsHelper.java:75)
at org.springframework.test.web.servlet.result.JsonPathResultMatchers.lambda$value$0(JsonPathResultMatchers.java:87)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:178)
at org.company.controllers.api.orders.OrderControllerTests.searchOrdersWithDoubleLessFilter(OrderControllerTests.java:536)

I had a look at code in Hamcrest and see that json parser tries being smart when converting json response from MockMvc to actual floating point type: if the number has less than 18 digits it is using Double, else BigDecimal is used. Which leads to inconsistent items in collection: some would be Double and some would be BigDecimal.

Is there a nice and clean way to work this around?

Thanks.

user1921819
  • 3,290
  • 2
  • 22
  • 28

1 Answers1

3

I worked this around with providing a custom matcher but do not like the solution myself. It looks like there should be a way to solve this in a more elegant way.

Validation call:

.andExpect(jsonPath("$.orders.[*].orderPrice", everyItem(applyConversion(lessThan(orderPrice), ConverterUtils::toDouble))));

Helpers:

public static <TObj, TConst extends java.lang.Comparable<TConst>> org.hamcrest.Matcher<TObj> applyConversion(org.hamcrest.Matcher<TConst> matcher, Function<TObj, TConst> converter) {
    return new TypeSafeMatcher<TObj>() {

        @Override
        public void describeTo(Description description) {
            matcher.describeTo(description);
        }

        @Override
        protected boolean matchesSafely(TObj item) {
            TConst obj = converter.apply(item);
            return matcher.matches(obj);
        }
    };
}


public static double toDouble(Object obj) {
    if (obj instanceof Number)
        return ((Number)obj).doubleValue();
    else if (obj instanceof String)
        return Double.parseDouble((String)obj);
    else
        throw new UnsupportedOperationException(String.format("Cannot convert %s to double", obj));
}
user1921819
  • 3,290
  • 2
  • 22
  • 28