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

satisfiesExactlyInAnyOrder fails if actual overrides equals #3339

Closed
PBoddington opened this issue Jan 19, 2024 · 4 comments
Closed

satisfiesExactlyInAnyOrder fails if actual overrides equals #3339

PBoddington opened this issue Jan 19, 2024 · 4 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@PBoddington
Copy link

PBoddington commented Jan 19, 2024

equals and hashCode are used in the implementation of satisfiesExactlyInAnyOrder (when they definitely aren't needed). This can result in incorrect assertion results.

  • assertj core version: 3.25.1
  • java version: 17
  • test framework version: n/a
  • os (if relevant):

Example of the issue:

public class MyTest {

  @Test
  void shouldPassButFails() {
    StringAndInt first = new StringAndInt("X", 1);
    StringAndInt second = new StringAndInt("X", 2);
    assertThat(List.of(first, second)).satisfiesExactlyInAnyOrder(
        stringAndInt -> assertThat(stringAndInt.integer).isEqualTo(2),
        stringAndInt -> assertThat(stringAndInt.integer).isEqualTo(1)
    );
  }

  private static final class StringAndInt {
    private final String string;
    private final int integer;

    private StringAndInt(
        String string,
        int integer
    ) {
      this.string = string;
      this.integer = integer;
    }

    @Override
    public int hashCode() {
      return string.hashCode();
    }

    @Override
    public boolean equals(Object o) {
      return (o instanceof StringAndInt other) && other.string.equals(string);
    }

    @Override
    public String toString() {
      return "StringAndInt{" +
          "string='" + string + '\'' +
          ", integer=" + integer +
          '}';
    }
  }
}

This test fails with

Expecting actual:
  [StringAndInt{string='X', integer=1}, StringAndInt{string='X', integer=2}]
to satisfy all the consumers in any order.

but should pass.

The problem is that the use of List::remove in the implementation results in the wrong element being removed.

In general we believe there are a number of bugs in assertj due to unnecessary usages of equals and hashCode, and think that the best way to weed them out is to have unit tests using classes where equals and hashCode throw exceptions.

@PBoddington PBoddington changed the title Bug in satisfiesExactlyInAnyOrder due to usages of equals and hashCode in the implementation Bug in satisfiesExactlyInAnyOrder due to usages of equals and hashCode in the implementation Jan 19, 2024
@scordio
Copy link
Member

scordio commented Jan 19, 2024

Thank you for reporting it, @PBoddington!

@scordio scordio added the type: bug A general bug label Jan 19, 2024
@scordio scordio added this to the 3.25.2 milestone Jan 19, 2024
@scordio scordio self-assigned this Jan 19, 2024
@scordio scordio changed the title Bug in satisfiesExactlyInAnyOrder due to usages of equals and hashCode in the implementation satisfiesExactlyInAnyOrder fails if actual overrides equals Jan 21, 2024
@scordio
Copy link
Member

scordio commented Jan 21, 2024

In general we believe there are a number of bugs in assertj due to unnecessary usages of equals and hashCode, and think that the best way to weed them out is to have unit tests using classes where equals and hashCode throw exceptions.

Do you have any specific examples in mind?

@PBoddington
Copy link
Author

Thanks for fixing so quickly!

In general we believe there are a number of bugs in assertj due to unnecessary usages of equals and hashCode, and think that the best way to weed them out is to have unit tests using classes where equals and hashCode throw exceptions.

Do you have any specific examples in mind?

One other thing was #3340 but it's hard to give other specific examples right now. This came about because as an experiment to see if people in our org were writing code relying on undocumented equals/hashCode behaviour of certain core classes, we changed the implementations to simply throw exceptions to see what tests failed. Many of the exceptions we saw were coming from assertj itself. The reason it's so difficult to say whether these are bugs or not is that it would not be a bug in itself for assertj to be calling equals/hashCode on the arguments passed. I'd have to go through the exceptions and find if equals/hashCode was being used correctly. In this particular case I was able to see quite quickly that it was a bug, but other exceptions require more analysis. I'll create more issues if I do find any more.

@scordio
Copy link
Member

scordio commented Jan 22, 2024

Thanks, Paul!

@joel-costigliola is already looking at #3340. I couldn't spot any other obvious case where the same mistake happened but we'll keep an eye on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants