1

I have this Groovy test for my Spring application.

given:
    def mapProperties = new JSONObject().put(
      "eligibility", "true").put(
      "group", "group1")
when:
    trackingService.sendUnifiedOnboardingAssignmentUserUpdate()
then:
    1 * trackingCollector.send(new UserUpdate(mapProperties))

Tracking Collector is external and defined as a Spring Bean in the Specification I am extending.

@SpringBean
TrackingCollector trackingCollector = Mock()

trackingService is in my domain layer and is Autowired

The test should run successfully, but it's not.

Too few invocations for:

1 * trackingCollector.send(new UserUpdate(mapProperties))   (0 invocations)

Unmatched invocations (ordered by similarity):

1 * trackingCollector.send(UserUpdate(super=Event(properties={"group":"group1","eligibility":"true"})))
One or more arguments(s) didn't match:
0: argument == expected
   |        |  |
   |        |  UserUpdate(super=Event(properties={"group":"group1","eligibility":"true"})) (com.package.tracking.collector.UserUpdate@4fe5aac9)
   |        false
   UserUpdate(super=Event(properties={"group":"group1","eligibility":"true"})) (com.package.tracking.collector.UserUpdate@48bb5a69)

The only different thing is the @..... But why is Groovy not checking the actual value of the object and chooses to compare the objects instead? What do I need to do to make the test pass in this case?

Eric
  • 329
  • 2
  • 20
  • Are `UserUpdate` and `Event` actually implementing `equals`, or are they using the default `Object.equals`? – Leonard Brünings Mar 30 '23 at 11:53
  • Very good question! @LeonardBrünings `UserUpdate` extends `Event` in a separate library. `UserUpdate` has `@EqualsAndHashCode(callSuper = true)` from Lombok, which means basically calling the superclass for equals. And then `Event` has `@Data` from Lombok, which also implements equals. – Eric Mar 30 '23 at 12:37
  • Either way, either `equals` or `hashCode` or both are not doing the thing you expect in your case. Where is your [MCVE](https://stackoverflow.com/help/mcve)? Nobody can debug your incomplete set of snippets. – kriegaex Mar 30 '23 at 15:38
  • The code base is extremely big, I did try to produce an MCVE, but without including a lot more information, I just don't see how it's possible. I've already added what Lombok annotations I use for the relevant classes. I suspect it's either a Lombok thing where `@Data` is not giving me the `equals` function I want, or a Groovy+Spock thing where the invocation parameter wants an exact match under the hood, comparing references, not values. I was looking for an answer that helped answer this specific question, as well as maybe help me fix it. – Eric Mar 30 '23 at 18:12
  • Spock uses [Groovy truth](https://groovy-lang.org/semantics.html#the-groovy-truth) so it will simply compare those two objects with `equals`. What happens if you manually construct two separate instances, and just do `assert a == b` on them? – Leonard Brünings Mar 30 '23 at 19:00
  • Doing that still fails the assertion. That's good news, the scope of the problem is narrowed down. So this means that defining another `equals` method that just checks every key+value pair of the `JSONObject` of `UserUpdate` in the library rather than using the Lombok basic `equals` (whatever that is) will probably fix my issue. I'll give this a go and update my question accordingly. Still wondering if there's a more elegant, Spock-only solution to this. Like, how do I make it check value of parameters of invocations over reference? – Eric Mar 30 '23 at 21:23
  • Well, as I said it doesn't do the Java `==`, but calls `equals`, and if that is properly implemented it will do what you want. If you don't want to properly implement it, then using [reflective equals](https://stackoverflow.com/a/9633089/2145769) could work for your use-case. – Leonard Brünings Mar 30 '23 at 23:43

1 Answers1

1

Spock uses Groovy truth, so it will simply compare those two objects with equals, or compareTo if they are implementing Comparable.

That the output contains the object id is a red-herring, Spock only includes it in the output when two objects have the same string rendering, to make it easier to figure out what is wrong.

If you can't fix the equals implementation, you could use a code argument constraint to manually check for the necessary values.

As noted in the comments your problems stems from the fact, that the equals method is not properly implemented for your use-case.

Leonard Brünings
  • 12,408
  • 1
  • 46
  • 66