AssertJ fails to assert BigDecimal equality without scale
Asked Answered
M

4

15

I am running assertions like so:

assertThat(obj.getTotal()).isEqualTo(BigDecimal.valueOf(4))

I am getting

Expecting: <4.00> to be equal to: <4>

so then I tried

assertThat(obj.getTotal()).isEqualTo(BigDecimal.valueOf(4.00))

Expecting: <4.00> to be equal to: <4.0>

I found a workaround in which I would set the scale of expected value 4 to 4.00, but it seems quite irritating that I have to do so for all BigDecimal variables in my tests. Is there a better way from AssertJ that I am unaware of?

Mella answered 2/9, 2020 at 23:9 Comment(1)
The underlying issue is not with AssertJ, but that BigDecimal has a weird definition of its equal method.Dentifrice
A
42

You can use isEqualByComparingTo or usingComparator that use BigDecimal.compareTo which compares the actual numeric value. The equals method which is the default assertj uses does the following according to JavaDoc:

Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).

So you can do:

    assertThat(obj.getTotal())
        .usingComparator(BigDecimal::compareTo)
        .isEqualTo(BigDecimal.valueOf(4.00));

or simply:

assertThat(obj.getTotal())
    .isEqualByComparingTo(BigDecimal.valueOf(4.00));

To use with Iterable and extracting assertions:

    assertThat(asList(new Trade("1", new BigDecimal("5.200")), new Trade("2", new BigDecimal("4.00"))))
        .extracting(Trade::getId, Trade::getAmount)
        .usingComparatorForType(BigDecimal::compareTo, BigDecimal.class)
        .usingRecursiveFieldByFieldElementComparator()
        .containsExactlyInAnyOrder(tuple("1", new BigDecimal("5.2")), tuple("2", new BigDecimal("4")));
Amoral answered 3/9, 2020 at 2:11 Comment(6)
This is exactly why isEqualByComparingTo assertion is available.Savick
This fails though when comparing an iterable with extracting with tuples :(Karoline
@Karoline You can use usingComparatorForType to assert Iterables with extracting. I have updated the answer with an example for it.Amoral
works flawlessly!Uptrend
Since 3.20.0 the comparison won't use any comparators set with: usingComparatorForType(Comparator, Class). Equivalent would be: usingRecursiveFieldByFieldElementComparator(RecursiveComparisonConfiguration.builder().withComparatorForType(BigDecimal::compareTo, BigDecimal.class).build())Malcolmmalcom
Note that using new BigDecimal("1345.31") did the trick for me instead of new BigDecimal(1345.31)Agreed
L
1

In case you are comparing elements in collections, since AssertJ 3.20.0, you must use usingRecursiveFieldByFieldElementComparator(RecursiveComparisonConfiguration):

import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;

import static java.util.Collections.singleton;
import static org.assertj.core.api.Assertions.assertThat;

public class BigDecimalTests {

static class Item {
    final BigDecimal length;

    public Item(BigDecimal length) {
        this.length = length;
    }

    static Item of(String length) {
        return new Item(new BigDecimal(length));
    }
}

@Test
void fail_1() {
    assertThat(singleton(Item.of("1.0")))
            .usingComparatorForType(BigDecimal::compareTo, BigDecimal.class)
            .containsExactly(Item.of("1"));
}

@Test
void fail_2() { // OK with AssertJ < 3.20.0
    assertThat(singleton(Item.of("1.0")))
            .usingComparatorForType(BigDecimal::compareTo, BigDecimal.class)
            .usingRecursiveFieldByFieldElementComparator()
            .containsExactly(Item.of("1"));
}

@Test
void fail_3() { // OK with AssertJ < 3.20.0
    assertThat(singleton(Item.of("1.0")))
            .usingRecursiveFieldByFieldElementComparator()
            .usingComparatorForType(BigDecimal::compareTo, BigDecimal.class)
            .containsExactly(Item.of("1"));
}

@Test
void ok() {
    assertThat(singleton(Item.of("1.0")))
            .usingRecursiveFieldByFieldElementComparator(
                    RecursiveComparisonConfiguration.builder()
                            .withComparatorForType(BigDecimal::compareTo, BigDecimal.class)
                            .build())
            .containsExactly(Item.of("1"));
}
}
Lekishalela answered 16/12, 2023 at 14:52 Comment(0)
M
1

Adding to @laurent's answer.

If you need a recursive comparison(eg you trying to compare two objects that have BigDecimal fields) the following worked for me with assertJ 3.17.1:

assertThat(objectWithBigDecimalField)
  .usingComparatorForType(BigDecimal::compareTo, BigDecimal.class)
  .usingRecursiveComparison()
  .isEqualTo(otherObjectWithBigDecimalField)
Monstrosity answered 1/2 at 11:1 Comment(0)
H
-3

Try java.math.BigDecimal.stripTrailingZeros() method, like this:

// AssertJ
assertThat(obj.getTotal().stripTrailingZeros()).isEqualTo(BigDecimal.valueOf(4));

// Junit
assertEquals(BigDecimal.valueOf(4), obj.getTotal().stripTrailingZeros());
Highflown answered 3/9, 2020 at 8:48 Comment(2)
Why did this answer get a negative score??Highflown
I don't know, seems to work in most cases; but you forgot about the engineering notation: stripTrailingZeros() does not help with BigDecimal("1E+2") != BigDecimal("100").Sistrunk

© 2022 - 2024 — McMap. All rights reserved.