-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Fixes #976: Resolve ambiguous constructors #980
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
Fixes #976: Resolve ambiguous constructors #980
Conversation
Codecov Report
@@ Coverage Diff @@
## release/2.x #980 +/- ##
=================================================
+ Coverage 86.8% 86.84% +0.04%
- Complexity 2275 2284 +9
=================================================
Files 287 287
Lines 5769 5787 +18
Branches 675 682 +7
=================================================
+ Hits 5008 5026 +18
Misses 571 571
Partials 190 190
Continue to review full report at Codecov.
|
I wonder why Codecov has no previous report to compare against. Do any of the maintainers have a clue? |
@mureinik Because commits by the delivery drone do |
@TimvdLippe That means that any PR that's rebased on top of a release won't have the benefit of a comparative CodeCov report. I get why the delivery drone should skip CI, but this is an unfortunate side effect. I'll bring this up on the mailing list - this PR probably isn't the right place to discuss it. And thanks for the explanation! |
@szczepiq ping? Will you have some time to take a look at this please? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementation looks fine to me. Could you add 1 more test to verify this also works correctly for primitive types? Just to make sure that keeps on working.
Thanks for your effort!!
ae4cb3e
to
63afde1
Compare
Rebased on top of 2.7.17 and added Note that this API does not support implicit promotions (e.g., passing an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really nice changes, thank you!
I really appreciate how cleanly and thoroughly you have described the PR. It is linked from our release notes and it makes Mockito documentation really good.
I requested changes but I don't consider them blocking. If you can get them done, I'm happy to merge right away :)
Thank you very much for the contribution!!!
@@ -130,4 +127,32 @@ private static boolean paramsMatch(Class<?>[] types, Object[] params) { | |||
} | |||
return true; | |||
} | |||
|
|||
private void evaluateConstructor(List<Constructor<?>> matchingConstructors, Constructor<?> constructor) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a javadoc describing this method? It has non trivial complexity and it would be handy to understand why we are implementing the algorithm this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, thanks.
I'll add some javadoc to explain this.
@@ -137,45 +137,90 @@ public void exception_message_when_constructor_not_found() { | |||
static class ExtendsExtendsBase extends ExtendsBase {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love the new tests. Thank you for adding comprehensive coverage!!!
"If you believe that Mockito could do a better join deciding on which constructor to use, please let us know.", | ||
"Ticket 685 contains the discussion and a workaround for ambiguous constructors using inner class.", | ||
"See https://github.com/mockito/mockito/issues/685" | ||
join("", " - ", constructors) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep the extra information. I think it is useful to:
- inform about the inner class pattern for working around the ambiguous constructors
- ask for feedback - it does not hurt to do that
You can keep the original or modify it if you want. I'd like to keep at least the hint about inner class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally took this message as a "workaround" until this PR is implemented, but you're right, let's keep it. Note there's a typo there ("join" instead of "job"). I'll fix that while I'm at it.
With the current code (introduced in Mockito 2.7.14 by commit 6a82c03), calling MockSettings#useConstructor with an argument list that would be applicable to more than one constructor would fail with an org.mockito.internal.creation.instance.InstantiationException. This behavior, however, is suboptimal, as described in issue mockito#976, as it makes useConstructor less robust than the Java compiler, which is able to resolve such ambiguities. With this patch, Mockito will attempt to match the constructor with the most specific parameter types. A constructor X is considered more specific than a constructor Y if: 1. They are both applicable to the given argument list 2. Constructor X has at least one parameter which is a further specialization of the corresponding parameter of constructor Y (i.e. paramX.isAssignableFrom(paramY)). 3. Constructor Y has no parameter which is a further specialization of the corresponding parameter of constructor X, as defined above. E.g., consider the following class: public class SomeClass { SomeClass(Object o) {} SomeClass(String s) {} } Without this patch, calling mock(SomeClass.class, withSettings().useConstructor("string!")) would fail. With this patch, such a call would invoke the SomeClass(String) constructor.
63afde1
to
b353d97
Compare
The text should read "a better JOB", not "a better JOIN".
b353d97
to
ed4e169
Compare
Pushed updated branch. Change from previous push:
|
The build failure seems to be a TravisCI issue, not an issue with the code itself, as this revision introduced only minor changes (added javadoc and fixed a typo) compared to the previous one, and some of the Travis checks are passing. The error:
@szczepiq @TimvdLippe Is there any way to re-trigger Travis on this PR? |
I have restarted the builds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice pull request, thanks for your effort!
With the current code (introduced in Mockito 2.7.14 by commit 6a82c03), calling
MockSettings.useConstructor
with an argument list that would be applicable to more than one constructor would fail with anorg.mockito.internal.creation.instance.InstantiationException
.This behavior, however, is suboptimal, as described in issue #976, as it makes
useConstructor
less robust than the Java compiler, which is able to resolve such ambiguities.With this patch, Mockito will attempt to match the constructor with the most specific parameter types. A constructor X is considered more specific than a constructor Y if:
paramX.isAssignableFrom(paramY)
).E.g., consider the following class:
Without this patch, calling
would fail. With this patch, such a call would invoke the
SomeClass(String)
constructor.As noted above, this PR fixes issue #976.