Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce assertEquals() variant with equality BiPredicate #2964

Closed
jbee opened this issue Jul 5, 2022 · 5 comments
Closed

Introduce assertEquals() variant with equality BiPredicate #2964

jbee opened this issue Jul 5, 2022 · 5 comments

Comments

@jbee
Copy link
Contributor

jbee commented Jul 5, 2022

Use case: comparing to BigDecimals for equality cannot rely on equals because mathematical equality is different from it being the exact same value with the same scale and precision.
On the other hand using

assertTrue(BigDecimal.valueOf(0.3d).compareTo(BigDecimal.valueOf(1.3-1d)) == 0);

gives a quite uselss error message:

org.opentest4j.AssertionFailedError: 
Expected :true
Actual   :false

Even when using custom message Supplier we cannot fix the expected/actual output.

This is why it would be useful to have:

<T> void assertEquals(T expected, T actual, BiPredicate<T, T> equals);

This could then be used with for example BigDecimal:

BiPredicate<BigDecimal, BigDecimal> mathematicalEquality = (a,b) -> a.compareTo(b) == 0;
assertEquals(BigDecimal.valueOf(0.3d), BigDecimal.valueOf(1.3-1d), mathematicalEquality);

Now the output of expected/actual is proper and no custom message supplier is needed.

org.opentest4j.AssertionFailedError: 
Expected :0.3
Actual   :0.30000000000000004

Note that choice of BiPredicate might just be the best the JDK has to offer.
When a dedicated interface would be defined this could just have one type parameter, for example:

interface BinaryPredicate<T> {
   boolean test(T a, T b);
}

I hope it also got clear that this was intentionally suggested as a generic solution that comes in handy in all kinds of use cases. The use case I gave was just one I recently encountered myself that made me think of this.

@sbrannen sbrannen changed the title assertEquals with equality function Introduce assertEquals() variant with equality BiPredicate Jul 5, 2022
@sbrannen
Copy link
Member

sbrannen commented Jul 5, 2022

Thanks to the proposal, @jbee.

Please note that we do have assertEquals variants for float and double that accept a delta, but we do not plan to support any other such variants.

In line with other team decisions (see #1920 (comment)), we will not include the proposed assertions in JUnit Jupiter.

@sbrannen sbrannen closed this as not planned Won't fix, can't repro, duplicate, stale Jul 5, 2022
@sormuras
Copy link
Member

sormuras commented Jul 5, 2022

In contrast to the explicitly typed proposal in

this proposal here offers a generic, functional and smart way to express custom equality with also providing a better expected/actual message for granted. With such a generic method in place future requests to add variant for other types could answered in a generic(!) way.

Reopening this issue for a team-discussion.

@marcphilipp
Copy link
Member

Team Decision: We think adding these methods might do more harm than good for the following reasons:

  • What is checked depends on the implementation of the BinaryPredicate and JUnit cannot enforce that it is an equality check
  • A Bi(nary)Predicate is not a Matcher and does not contribute to the failure message
  • Readability of the assertion can be impaired, e.g. assertEquals(4d, 4L, BinaryPredicate.compare(Comparator.comparingDouble(Number::doubleValue))) compared to the APIs provided by other popular assertion frameworks.

We'll consider making the formatting of assertion failure messages accessible in #2967, though.

@marcphilipp marcphilipp closed this as not planned Won't fix, can't repro, duplicate, stale Jul 8, 2022
@jbee
Copy link
Contributor Author

jbee commented Jul 8, 2022

I'll think you missed the point of

assertEquals(4d, 4L, BinaryPredicate.compare(Comparator.comparingDouble(Number::doubleValue)));

It is comparing a double with a long using the doubleValue(). This is just an example of how existing building blocks in the JRE can be used to build equality functions. Existing libraries would not support such a comparison because expected and actual are not of the same type.

@scordio
Copy link
Contributor

scordio commented Jul 8, 2022

Existing libraries would not support such a comparison because expected and actual are not of the same type.

Have you considered AssertJ's usingComparator? This usually gives enough flexibility for these use cases.

Considering your last example, this would work:

Comparator<Number> comparator = Comparator.comparingDouble(Number::doubleValue);

assertThat(4L)
  .usingComparator(comparator)
  .isEqualTo(4.0d);

Interestingly, this doesn't:

assertThat(4L)
  .usingComparator(Comparator.comparingDouble(Number::doubleValue))
  .isEqualTo(4.0d);

I assume something goes wrong with generics signatures so I've raised assertj/assertj#2702.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants