简体   繁体   中英

Confusion over REST Assured floating-point comparisons

The REST Assured usage documentation has examples like this:

get("/lotto").then().body("lotto.lottoId", equalTo(5));

OK, so they are using a Hamcrest matcher to compare to the int value 5 .

But they have a section saying that the REST Assured JSON parser by default uses float and not double , so instead of comparing to 12.12 I should compare to 12.12f :

get("/price").then().body("price", is(12.12f));

Wait, so how did 5 work above, which is an int and not a double ? Does the JSON parser use different primitives for integer and non-integer values?

But it gets more confusing. Well-versed programmers know that you shouldn't be comparing floating point values directly anyway (because of the intricacies of how floating-point values are stored, etc.). Instead you should use Matchers.closeTo(double operand, double error) which provides a margin of error. This is the correct way to do it. But wait --- even if I pass in 12.12f to Matchers.closeTo(double operand, double error), isn't it still going to convert it to a double ? Will this work with REST Assured?

I'm not 100% sure I'm correct here, but this post became too long for a comment...

From reading the docs for Hamcrest and REST Assured it seems like equalTo only returns true in the cases that Object.equals returns true:

[equalTo] Creates a matcher that matches when the examined object is logically equal to the specified operand, as determined by calling the Object.equals(java.lang.Object) method on the examined object.

Thus since REST Assured represents floating point values as floats, and Double.equals can only return true if that other object is a Double , it is necessary to use a float and not a double (as the input will get boxed into an object).

Also, the section of floats in the REST Assured docs seems to indicate it only applies to floating point values:

Floating point numbers must be compared with a Java "float" primitive.

I presume that means that integers are represented properly as Integers. (Other examples in the docs also seems to suggest this)

If you chose to use Matchers.closeTo instead of equalTo or is (which itself calls equalTo ), then it shouldn't matter if you use a double or a float .

You can compare a lot of things with equalTo of hamcrest.Matchers, it's used also to assert in test:

assertThat(longValue, equalTo(10L));
assertThat(cadena, equalTo("Esta es una cadena")); 

There is a comparison to Long and String, and of course, you have the closeTo to compare things such as doubles or bigdecimal, take a look here and there

So indeed, is normal the surprise, but you can also choose to compare or do equals when is convenient

Looks like you are mixing two things together.

Does the JSON parser use different primitives for integer and non-integer values?

Value comparison is done by given matcher object. The given matcher does not have any influence on the Json Parser in anyway. Irrespective of what value you have given to matcher ( 5 in first example) and what value would return by the given jsonpath, The equals matcher( org.hamcrest.core.IsEqual ) compare both valu by invoking Objects.equals() method. Also Json parser works with value objects and not with primitives.

so how did 5 work above, which is an int and not a double?

Assuming JsonPath lotto.lottoId would return int value, thus body("lotto.lottoId", equalTo(5)); will be true (obviously json value has to be 5 )

12.2' and 12.2f' are treated as same values in your example. Suffixing 'f' in your example is redundant and has no effect. Since the method closeTo(double, double) has defined the arguments types as double, the passed float values would get promoted to double types implicitly.

Since you know you json's value going to be double, So you could expression your assertion as:

get("/price").then().body("price", closeTo(12.12, 0.01));

Depending of the Json parser configuration, non-integer value could be read as BigDecimal, If that is the case, you can use variance of closeTo() as

.get("/price").then().body("price", 
           closeTo(BigDecimal.valueOf(12.12), BigDecimal.valueOf(0.01)))

Hope it helps.

If you carefully read Hamcrest then you'll find, it is clearly stated " when the examined object is logically equal to the specified operand " for equalTo . So obviously it does not matter if it is "5" or 5 ! ANd in Rest-Assured , It is mentioned that equalTo and hasItems are Hamcrest matchers which you should statically import from org.hamcrest.Matchers. So I don't think there should be any confusion.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM