Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compare two lists of double in JUnit

In a JUnit 4 test, I have a method getValues() that returns a List<Double> object that I want to compare with a reference list. Up to now, the best solution I've found was to use org.hamcrest.collection.IsArray.hasItems and org.hamcrest.Matchers.closeTo like this:

assertThat(getValues(), hasItems(closeTo(0.2, EPSILON), closeTo(0.3, EPSILON)));

This goes well for a test that returns only few values. But if a test returns more values, this is definitely not the best approach.

I also tried the following code. The downcast to Matcher before hasItems is required for the code to compile:

List<Matcher<Double>> doubleMatcherList = new ArrayList<Matcher<Double>>();
doubleMatcherList.add(closeTo(0.2, EPSILON));
doubleMatcherList.add(closeTo(0.3, EPSILON));
assertThat(getValues(), (Matcher) hasItems(doubleMatcherList));

The comparison failed and I don't understand why :

java.lang.AssertionError: Expected: (a collection containing <[a numeric value within <1.0E-6> of <0.2>, a numeric value within <1.0E-6> of <0.3>]>) got: <[0.2, 0.30000000000000004]>

Is there a better way to compare two large lists of doubles? The difficulty here is that a numerical tolerance is required to validate if the result of getValues() is equal to my reference list. This kind of comparison seems very easy for any lists of objects, but not with lists of Double.

like image 477
Francois Avatar asked Mar 15 '16 15:03

Francois


2 Answers

I think the right solution here would be a Custom Matcher. Basically something like IsIterableContainingInOrder that only works on Doubles and supports an error margin.

like image 91
Michael Lloyd Lee mlk Avatar answered Oct 04 '22 10:10

Michael Lloyd Lee mlk


If you are willing to convert from List<Double> to double[], assertArrayEquals allows specifying a tolerance for error:

assertArrayEquals(new double[] {1, 2}, new double[] {1.01, 2.09}, 1E-1);

In Java 8, the conversion from list to array is relatively clean (see related question). For example:

double[] toArray(List<Double> list) {
    return list.stream().mapToDouble(Number::doubleValue).toArray();
}

And the assert statement could then be as follows:

assertArrayEquals(toArray(refList), toArray(getValues()), 1E-9);

p.s. toArray can be made to work with any Number type by just changing the signature to double[] toArray(List<? extends Number> list).

like image 22
teichert Avatar answered Oct 04 '22 10:10

teichert