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
Interaction argument-matching treats all Mocks/Stubs of Comparables as equivalent #1322
Comments
`EqualArgumentConstraint` uses `DefaultTypeTransformation.compareEqual` to check arguments' equality in interactions. For most objects, this is equivalent to calling `.equals`, so Java Mocks come with an intuitive default implemention of `.equals`. But for objects that are `Comparable` (including Mocks of types that implement `Comparable`), `DefaultTypeTransformation.compareEqual(x, y)` is equivalent to calling `x.compareTo(y) == 0`. As a result, all Mocks of Comparable types are treated as equal by `EqualArgumentConstraint`. This commit fixes that by adding a default implementation of `.compareTo` to `Comparable` Mocks.
The contract of The underlying problem is that you've used a mock without also mocking the method you are using, so by default it returns If you add |
This is "strongly recommended" but not a requirement. For example, Anyway, in Spock's current form, Mocks of Comparables do not meet the requirement you're describing - if
That's what the documentation says, but in practice that's not what Spock is doing.
I'm not "using"
Yeah, that's the painful tradeoff here. I think that the current behavior of I haven't looked too deeply into the possibility of messing with |
Yes, mocks don't really adhere to any contract unless you tell them to. You actually will get a NPE if you replace
Let me rephrase, Spocks The PR #1323 would change the default behavior of all mocks, not just in the Any thoughts @spockframework/supporter ? |
While I understand the problem with the provided test case, I fail to imagine the real-world examples, in which this becomes an issue and this might be a smell with regards to test design. However, of course, Spock should adhere to some kind of reasonable contract. Since this behaviour is reported upstream and consistent with Groovy (and it does not look like it will change there), I don't think Spock should diverge from this. Maybe one could clarify in the docs, that Spock relies on the |
I checked the documentation and I think it already clearly states:
We could add a note for the |
FWIW, I've just hit this issue. Good to see it fixed in 2.3! |
Issue description
Hi! It looks like interaction argument-matching treats all Mocks/Stubs of classes that implement Comparable as equivalent to one another.
How to reproduce
Here's a test case to repro this:
This test fails because
bar.foo(baz)
actually matchesbar.foo(qux)
. This wouldn't be true ifbaz
,qux
, and the argument tofoo
weren'tComparable
.This probably has something to do with the way that Groovy
==
aliases.equals
for all objects except forComparable
s -==
meanscompareTo
when used withComparable
s. (I don't know much about Groovy, so this was a big surprise for me; if you're in that boat, see this issue for more information.) More specifically, it seems to me thatEqualArgumentConstraint
's call toGroovyRuntimeUtil.equals
ends up calling the same logic that Groovy uses when it sees a==
. Generally speaking, I think that's intuitive, but in this case you end up with broken-looking default behavior.I have a commit that fixes this and will make a PR soon. I ended up adding another
DefaultInteraction
toDefaultJavaLangObjectInteractions
that makescompareTo
compareSystem.identityHashcode
for any mocks that areinstanceof Comparable
. That felt cleaner than adding more complexity toEqualArgumentConstraint
in order to handle the special case whereexpected
is a mock of aComparable
.Link to a gist or similar (optional)
PR to follow with a test that reproduces the bug.
Additional Environment information
I used IntelliJ IDEA on macOS. My global Java version is openjdk 1.8.0_232. The versions of the other tools are whatever the Spock project defaults to after a
./gradlew build
and./gradlew cleanIdea idea
run - I don't think I have them installed globally.Java/JDK
java -version
Groovy version
groovy -version
I don't have groovy installed globally.
Build tool version
Gradle
gradle -version
Apache Maven
mvn -version
I don't have this installed globally.
Operating System
IDE
Build-tool dependencies used
Whatever's in this project's
build.gradle
? I don't use Gradle for work, so I don't really know how to fill out this section.The text was updated successfully, but these errors were encountered: