diff --git a/.dependabot/config.yml b/.dependabot/config.yml
new file mode 100644
index 0000000000..cd85cf642e
--- /dev/null
+++ b/.dependabot/config.yml
@@ -0,0 +1,9 @@
+version: 1
+
+update_configs:
+ - package_manager: "java:gradle"
+ directory: "/"
+ update_schedule: "daily"
+ # Redundant - default repository branch by default
+ target_branch: "release/2.x"
+
diff --git a/build.gradle b/build.gradle
index 0dac0c72f3..4285ff16ff 100644
--- a/build.gradle
+++ b/build.gradle
@@ -11,7 +11,7 @@ buildscript {
classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.6'
//Using buildscript.classpath so that we can resolve shipkit from maven local, during local testing
- classpath 'org.shipkit:shipkit:2.0.28'
+ classpath 'org.shipkit:shipkit:2.1.6'
}
}
@@ -110,7 +110,7 @@ subprojects {
name = rootProject.name + '-' + project.name
}
}
-
+
afterEvaluate {
def lib = publishing.publications.javaLibrary
if(lib && !lib.artifactId.startsWith("mockito-")) {
diff --git a/doc/release-notes/official.md b/doc/release-notes/official.md
index 58570c9b1e..752360b3c8 100644
--- a/doc/release-notes/official.md
+++ b/doc/release-notes/official.md
@@ -1,5 +1,53 @@
*Release notes were automatically generated by [Shipkit](http://shipkit.org/)*
+#### 2.24.10
+ - 2019-03-05 - [4 commits](https://github.com/mockito/mockito/compare/v2.24.9...v2.24.10) by [Tim van der Lippe](https://github.com/TimvdLippe) - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.10-green.svg)](https://bintray.com/mockito/maven/mockito-development/2.24.10)
+ - Prevent NPE in findTypeFromGenericInArguments [(#1648)](https://github.com/mockito/mockito/pull/1648)
+
+#### 2.24.9
+ - 2019-03-04 - [12 commits](https://github.com/mockito/mockito/compare/v2.24.7...v2.24.9) by 6 authors - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.9-green.svg)](https://bintray.com/mockito/maven/mockito-development/2.24.9)
+ - Commits: [Brice Dutheil](https://github.com/bric3) (5), [Tim van der Lippe](https://github.com/TimvdLippe) (3), [epeee](https://github.com/epeee) (1), [Fr Jeremy Krieg](https://github.com/kriegfrj) (1), [Paweł Pamuła](https://github.com/PawelPamula) (1), shipkit-org (1)
+ - [Java 9 support] ClassCastExceptions with JDK9 javac [(#357)](https://github.com/mockito/mockito/issues/357)
+ - [Bugfixes] RETURNS_DEEP_STUBS causes "Raw extraction not supported for : 'null'" in some cases [(#1621)](https://github.com/mockito/mockito/issues/1621)
+ - Update shipkit plugin (v2.1.6) [(#1647)](https://github.com/mockito/mockito/pull/1647)
+ - VerificationCollector to handle non-matching args and other assertions [(#1644)](https://github.com/mockito/mockito/pull/1644)
+ - VerificationCollector doesn't work for invocations with non-matching args [(#1642)](https://github.com/mockito/mockito/issues/1642)
+ - Fix returns mocks for final classes [(#1641)](https://github.com/mockito/mockito/pull/1641)
+ - Removes inaccessible links from javadocs in Mockito.java [(#1639)](https://github.com/mockito/mockito/pull/1639)
+ - Mockito.java contains inaccessible links to articles. [(#1638)](https://github.com/mockito/mockito/issues/1638)
+ - Handle terminal type var with bounds [(#1624)](https://github.com/mockito/mockito/pull/1624)
+ - Return null instead of causing a CCE [(#1612)](https://github.com/mockito/mockito/pull/1612)
+
+#### 2.24.7
+ - 2019-02-28 - [2 commits](https://github.com/mockito/mockito/compare/v2.24.6...v2.24.7) by [shipkit-org](https://github.com/shipkit-org) (1), [Tim van der Lippe](https://github.com/TimvdLippe) (1) - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.7-green.svg)](https://bintray.com/mockito/maven/mockito-development/2.24.7)
+ - Fix handling of generics in ReturnsMocks [(#1635)](https://github.com/mockito/mockito/pull/1635)
+
+#### 2.24.6
+ - 2019-02-27 - [16 commits](https://github.com/mockito/mockito/compare/v2.24.5...v2.24.6) by [Szczepan Faber](https://github.com/mockitoguy) (15), [Marcin Stachniuk](https://github.com/mstachniuk) (1) - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.6-green.svg)](https://bintray.com/mockito/maven/mockito-development/2.24.6)
+ - Make use of Shipkit v2.1.3 [(#1626)](https://github.com/mockito/mockito/pull/1626)
+ - Exposed new API - StubbingLookupListener [(#1543)](https://github.com/mockito/mockito/pull/1543)
+
+#### 2.24.5
+ - 2019-02-18 - [2 commits](https://github.com/mockito/mockito/compare/v2.24.4...v2.24.5) by [Tim van der Lippe](https://github.com/TimvdLippe) - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.5-green.svg)](https://bintray.com/mockito/maven/mockito/2.24.5)
+ - No pull requests referenced in commit messages.
+
+#### 2.24.4
+ - 2019-02-13 - [1 commit](https://github.com/mockito/mockito/compare/v2.24.3...v2.24.4) by [Alex Simkin](https://github.com/SimY4) - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.4-green.svg)](https://bintray.com/mockito/maven/mockito-development/2.24.4)
+ - Fixes #1618 : Fix strict stubbing profile serialization support. [(#1620)](https://github.com/mockito/mockito/pull/1620)
+ - Serializable flag doesn't make mock serializable [(#1618)](https://github.com/mockito/mockito/issues/1618)
+
+#### 2.24.3
+ - 2019-02-12 - [2 commits](https://github.com/mockito/mockito/compare/v2.24.2...v2.24.3) by [Marcin Zajączkowski](https://github.com/szpak) (1), [Tim van der Lippe](https://github.com/TimvdLippe) (1) - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.3-green.svg)](https://bintray.com/mockito/maven/mockito-development/2.24.3)
+ - [Java 9 support] ClassCastExceptions with JDK9 javac [(#357)](https://github.com/mockito/mockito/issues/357)
+ - Return null instead of causing a CCE [(#1612)](https://github.com/mockito/mockito/pull/1612)
+ - Automatic dependency update with Dependabot [(#1600)](https://github.com/mockito/mockito/pull/1600)
+ - Fix/bug 1551 cce on smart not null answers [(#1576)](https://github.com/mockito/mockito/pull/1576)
+
+#### 2.24.2
+ - 2019-02-11 - [1 commit](https://github.com/mockito/mockito/compare/v2.24.1...v2.24.2) by [Tim van der Lippe](https://github.com/TimvdLippe) - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.2-green.svg)](https://bintray.com/mockito/maven/mockito-development/2.24.2)
+ - Fix issue with mocking of java.util.* classes [(#1617)](https://github.com/mockito/mockito/pull/1617)
+ - Issue with mocking type in "java.util.*", Java 12 [(#1615)](https://github.com/mockito/mockito/issues/1615)
+
#### 2.24.1
- 2019-02-04 - [1 commit](https://github.com/mockito/mockito/compare/v2.24.0...v2.24.1) by [zoujinhe](https://github.com/zoujinhe) - published to [![Bintray](https://img.shields.io/badge/Bintray-2.24.1-green.svg)](https://bintray.com/mockito/maven/mockito-development/2.24.1)
- typo? ... 'thenReturn' instruction if completed -> ... 'thenReturn' instruction is completed [(#1608)](https://github.com/mockito/mockito/pull/1608)
diff --git a/gradle/errorprone.gradle b/gradle/errorprone.gradle
index 1d9cf7428e..43c8633fcd 100644
--- a/gradle/errorprone.gradle
+++ b/gradle/errorprone.gradle
@@ -10,3 +10,7 @@ if (JavaVersion.current() == JavaVersion.VERSION_1_8) {
dependencies {
errorprone libraries.errorprone
}
+
+tasks.named("compileTestJava").configure {
+ options.errorprone.errorproneArgs << "-Xep:MockitoCast:OFF"
+}
diff --git a/src/main/java/org/mockito/MockSettings.java b/src/main/java/org/mockito/MockSettings.java
index c79c243175..95fcfe3236 100644
--- a/src/main/java/org/mockito/MockSettings.java
+++ b/src/main/java/org/mockito/MockSettings.java
@@ -9,6 +9,7 @@
import org.mockito.invocation.InvocationFactory;
import org.mockito.invocation.MockHandler;
import org.mockito.listeners.InvocationListener;
+import org.mockito.listeners.StubbingLookupListener;
import org.mockito.listeners.VerificationStartedListener;
import org.mockito.mock.MockCreationSettings;
import org.mockito.mock.SerializableMode;
@@ -203,6 +204,24 @@ public interface MockSettings extends Serializable {
*/
MockSettings verboseLogging();
+ /**
+ * Add stubbing lookup listener to the mock object.
+ *
+ * Multiple listeners may be added and they will be notified orderly.
+ *
+ * For use cases and more info see {@link StubbingLookupListener}.
+ *
+ * Example:
+ *
+ * List mockWithListener = mock(List.class, withSettings().stubbingLookupListeners(new YourStubbingLookupListener()));
+ *
+ *
+ * @param listeners The stubbing lookup listeners to add. May not be null.
+ * @return settings instance so that you can fluently specify other settings
+ * @since 2.24.6
+ */
+ MockSettings stubbingLookupListeners(StubbingLookupListener... listeners);
+
/**
* Registers a listener for method invocations on this mock. The listener is
* notified every time a method on this mock is called.
diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java
index 2e10eca426..5066eec3d7 100644
--- a/src/main/java/org/mockito/Mockito.java
+++ b/src/main/java/org/mockito/Mockito.java
@@ -208,7 +208,7 @@
*
* //Although it is possible to verify a stubbed invocation, usually it's just redundant
* //If your code cares what get(0) returns, then something else breaks (often even before verify() gets executed).
- * //If your code doesn't care what get(0) returns, then it should not be stubbed. Not convinced? See here .
+ * //If your code doesn't care what get(0) returns, then it should not be stubbed.
* verify(mockedList).get(0);
*
*
@@ -427,8 +427,7 @@
* Some users who did a lot of classic, expect-run-verify mocking tend to use verifyNoMoreInteractions()
very often, even in every test method.
* verifyNoMoreInteractions()
is not recommended to use in every test method.
* verifyNoMoreInteractions()
is a handy assertion from the interaction testing toolkit. Use it only when it's relevant.
- * Abusing it leads to overspecified , less maintainable tests. You can find further reading
- * here .
+ * Abusing it leads to overspecified , less maintainable tests.
*
*
* See also {@link Mockito#never()} - it is more explicit and
@@ -599,8 +598,7 @@
* Before the release 1.8 , Mockito spies were not real partial mocks.
* The reason was we thought partial mock is a code smell.
* At some point we found legitimate use cases for partial mocks
- * (3rd party interfaces, interim refactoring of legacy code, the full article is
- * here )
+ * (3rd party interfaces, interim refactoring of legacy code).
*
*
*
@@ -709,8 +707,7 @@
*
*
* Finally, after many internal debates & discussions on the mailing list, partial mock support was added to Mockito.
- * Previously we considered partial mocks as code smells. However, we found a legitimate use case for partial mocks - more reading:
- * here
+ * Previously we considered partial mocks as code smells. However, we found a legitimate use case for partial mocks.
*
* Before release 1.8 spy()
was not producing real partial mocks and it was confusing for some users.
* Read more about spying: here or in javadoc for {@link Mockito#spy(Object)} method.
@@ -1695,6 +1692,7 @@ public class Mockito extends ArgumentMatchers {
/**
* Optional Answer
to be used with {@link Mockito#mock(Class, Answer)}
+ *
*
* {@link Answer} can be used to define the return values of unstubbed invocations.
*
@@ -1726,8 +1724,11 @@ public class Mockito extends ArgumentMatchers {
*
*
*
- * Note: Stubbing partial mocks using when(mock.getSomething()).thenReturn(fakeValue)
+ * Note 1: Stubbing partial mocks using when(mock.getSomething()).thenReturn(fakeValue)
* syntax will call the real method. For partial mock it's recommended to use doReturn
syntax.
+ *
+ * Note 2: If the mock is serialized then deserialized, then this answer will not be able to understand
+ * generics metadata.
*/
public static final Answer CALLS_REAL_METHODS = Answers.CALLS_REAL_METHODS;
@@ -2080,7 +2081,6 @@ public static T spy(Class classToSpy) {
* Let's say you've stubbed foo.bar()
.
* If your code cares what foo.bar()
returns then something else breaks(often before even verify()
gets executed).
* If your code doesn't care what get(0)
returns then it should not be stubbed.
- * Not convinced? See here .
*
*
* See examples in javadoc for {@link Mockito} class
@@ -2112,7 +2112,6 @@ public static OngoingStubbing when(T methodCall) {
* Let's say you've stubbed foo.bar()
.
* If your code cares what foo.bar()
returns then something else breaks(often before even verify()
gets executed).
* If your code doesn't care what get(0)
returns then it should not be stubbed.
- * Not convinced? See here .
*
*
* See examples in javadoc for {@link Mockito} class
@@ -2214,8 +2213,7 @@ public static void clearInvocations(T ... mocks) {
* Some users who did a lot of classic, expect-run-verify mocking tend to use verifyNoMoreInteractions()
very often, even in every test method.
* verifyNoMoreInteractions()
is not recommended to use in every test method.
* verifyNoMoreInteractions()
is a handy assertion from the interaction testing toolkit. Use it only when it's relevant.
- * Abusing it leads to overspecified, less maintainable tests. You can find further reading
- * here .
+ * Abusing it leads to overspecified, less maintainable tests.
*
* This method will also detect unverified invocations that occurred before the test method,
* for example: in setUp()
, @Before
method or in constructor.
diff --git a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java
index ca67730793..50134b4458 100644
--- a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java
+++ b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java
@@ -11,6 +11,7 @@
import org.mockito.internal.util.MockCreationValidator;
import org.mockito.internal.util.MockNameImpl;
import org.mockito.listeners.InvocationListener;
+import org.mockito.listeners.StubbingLookupListener;
import org.mockito.listeners.VerificationStartedListener;
import org.mockito.mock.MockCreationSettings;
import org.mockito.mock.MockName;
@@ -19,17 +20,17 @@
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import static java.util.Arrays.asList;
import static org.mockito.internal.exceptions.Reporter.defaultAnswerDoesNotAcceptNullParameter;
import static org.mockito.internal.exceptions.Reporter.extraInterfacesAcceptsOnlyInterfaces;
import static org.mockito.internal.exceptions.Reporter.extraInterfacesDoesNotAcceptNullParameters;
import static org.mockito.internal.exceptions.Reporter.extraInterfacesRequiresAtLeastOneInterface;
-import static org.mockito.internal.exceptions.Reporter.invocationListenersRequiresAtLeastOneListener;
import static org.mockito.internal.exceptions.Reporter.methodDoesNotAcceptParameter;
+import static org.mockito.internal.exceptions.Reporter.requiresAtLeastOneListener;
import static org.mockito.internal.util.collections.Sets.newSet;
@SuppressWarnings("unchecked")
@@ -154,7 +155,7 @@ public Object[] getConstructorArgs() {
}
List resultArgs = new ArrayList(constructorArgs.length + 1);
resultArgs.add(outerClassInstance);
- resultArgs.addAll(Arrays.asList(constructorArgs));
+ resultArgs.addAll(asList(constructorArgs));
return resultArgs.toArray(new Object[constructorArgs.length + 1]);
}
@@ -173,17 +174,23 @@ public MockSettings verboseLogging() {
@Override
public MockSettings invocationListeners(InvocationListener... listeners) {
- if (listeners == null || listeners.length == 0) {
- throw invocationListenersRequiresAtLeastOneListener();
- }
addListeners(listeners, invocationListeners, "invocationListeners");
return this;
}
- private static void addListeners(T[] listeners, List container, String method) {
+ @Override
+ public MockSettings stubbingLookupListeners(StubbingLookupListener... listeners) {
+ addListeners(listeners, stubbingLookupListeners, "stubbingLookupListeners");
+ return this;
+ }
+
+ static void addListeners(T[] listeners, List container, String method) {
if (listeners == null) {
throw methodDoesNotAcceptParameter(method, "null vararg array.");
}
+ if (listeners.length == 0) {
+ throw requiresAtLeastOneListener(method);
+ }
for (T listener : listeners) {
if (listener == null) {
throw methodDoesNotAcceptParameter(method, "null listeners.");
@@ -207,13 +214,8 @@ private boolean invocationListenersContainsType(Class> clazz) {
return false;
}
- @Override
- public List getInvocationListeners() {
- return this.invocationListeners;
- }
-
public boolean hasInvocationListeners() {
- return !invocationListeners.isEmpty();
+ return !getInvocationListeners().isEmpty();
}
@Override
diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java
index 906692768d..e57a82e739 100644
--- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java
+++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java
@@ -46,13 +46,11 @@ Object doIntercept(Object mock,
Method invokedMethod,
Object[] arguments,
RealMethod realMethod) throws Throwable {
- return doIntercept(
- mock,
- invokedMethod,
- arguments,
- realMethod,
- new LocationImpl()
- );
+ return doIntercept(mock,
+ invokedMethod,
+ arguments,
+ realMethod,
+ new LocationImpl());
}
Object doIntercept(Object mock,
@@ -108,11 +106,11 @@ public static Object interceptSuperCallable(@This Object mock,
return superCall.call();
}
return interceptor.doIntercept(
- mock,
- invokedMethod,
- arguments,
- new RealMethod.FromCallable(superCall)
- );
+ mock,
+ invokedMethod,
+ arguments,
+ new RealMethod.FromCallable(superCall)
+ );
}
@SuppressWarnings("unused")
@@ -126,11 +124,11 @@ public static Object interceptAbstract(@This Object mock,
return stubValue;
}
return interceptor.doIntercept(
- mock,
- invokedMethod,
- arguments,
- RealMethod.IsIllegal.INSTANCE
- );
+ mock,
+ invokedMethod,
+ arguments,
+ RealMethod.IsIllegal.INSTANCE
+ );
}
}
}
diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java
index 8968b9e8e4..093978dd22 100644
--- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java
+++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java
@@ -98,7 +98,7 @@ public Class extends T> mockClass(MockFeatures features) {
if (localMock || loader instanceof MultipleParentClassLoader && !isComingFromJDK(features.mockedType)) {
typeName = features.mockedType.getName();
} else {
- typeName = InjectionBase.class.getPackage().getName() + features.mockedType.getSimpleName();
+ typeName = InjectionBase.class.getPackage().getName() + "." + features.mockedType.getSimpleName();
}
String name = String.format("%s$%s$%d", typeName, "MockitoMock", Math.abs(random.nextInt()));
diff --git a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java
index 81f52f982b..3b45592c72 100644
--- a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java
+++ b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java
@@ -4,8 +4,8 @@
*/
package org.mockito.internal.creation.settings;
-import org.mockito.internal.listeners.StubbingLookupListener;
import org.mockito.listeners.InvocationListener;
+import org.mockito.listeners.StubbingLookupListener;
import org.mockito.listeners.VerificationStartedListener;
import org.mockito.mock.MockCreationSettings;
import org.mockito.mock.MockName;
@@ -18,6 +18,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
public class CreationSettings implements MockCreationSettings, Serializable {
private static final long serialVersionUID = -6789800638070123629L;
@@ -30,7 +31,11 @@ public class CreationSettings implements MockCreationSettings, Serializabl
protected MockName mockName;
protected SerializableMode serializableMode = SerializableMode.NONE;
protected List invocationListeners = new ArrayList();
- protected final List stubbingLookupListeners = new ArrayList();
+
+ //Other listeners in this class may also need concurrency-safe implementation. However, no issue was reported about it.
+ // If we do it, we need to understand usage patterns and choose the right concurrent implementation.
+ protected List stubbingLookupListeners = new CopyOnWriteArrayList();
+
protected List verificationStartedListeners = new LinkedList();
protected boolean stubOnly;
protected boolean stripAnnotations;
@@ -43,6 +48,7 @@ public CreationSettings() {}
@SuppressWarnings("unchecked")
public CreationSettings(CreationSettings copy) {
+ //TODO can we have a reflection test here? We had a couple of bugs here in the past.
this.typeToMock = copy.typeToMock;
this.extraInterfaces = copy.extraInterfaces;
this.name = copy.name;
@@ -51,6 +57,7 @@ public CreationSettings(CreationSettings copy) {
this.mockName = copy.mockName;
this.serializableMode = copy.serializableMode;
this.invocationListeners = copy.invocationListeners;
+ this.stubbingLookupListeners = copy.stubbingLookupListeners;
this.verificationStartedListeners = copy.verificationStartedListeners;
this.stubOnly = copy.stubOnly;
this.useConstructor = copy.isUsingConstructor();
diff --git a/src/main/java/org/mockito/internal/exceptions/Reporter.java b/src/main/java/org/mockito/internal/exceptions/Reporter.java
index 3158228f22..1b68fbb657 100644
--- a/src/main/java/org/mockito/internal/exceptions/Reporter.java
+++ b/src/main/java/org/mockito/internal/exceptions/Reporter.java
@@ -684,8 +684,8 @@ public static MockitoException methodDoesNotAcceptParameter(String method, Strin
return new MockitoException(method + "() does not accept " + parameter + " See the Javadoc.");
}
- public static MockitoException invocationListenersRequiresAtLeastOneListener() {
- return new MockitoException("invocationListeners() requires at least one listener");
+ public static MockitoException requiresAtLeastOneListener(String method) {
+ return new MockitoException(method + "() requires at least one listener");
}
public static MockitoException invocationListenerThrewException(InvocationListener listener, Throwable listenerThrowable) {
diff --git a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java
index 21212a6754..b5d2022b39 100644
--- a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java
+++ b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java
@@ -5,13 +5,14 @@
package org.mockito.internal.junit;
import org.mockito.internal.exceptions.Reporter;
-import org.mockito.internal.listeners.StubbingLookupEvent;
-import org.mockito.internal.listeners.StubbingLookupListener;
import org.mockito.internal.stubbing.UnusedStubbingReporting;
import org.mockito.invocation.Invocation;
+import org.mockito.listeners.StubbingLookupEvent;
+import org.mockito.listeners.StubbingLookupListener;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Stubbing;
+import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
@@ -22,7 +23,9 @@
* Default implementation of stubbing lookup listener.
* Fails early if stub called with unexpected arguments, but only if current strictness is set to STRICT_STUBS.
*/
-class DefaultStubbingLookupListener implements StubbingLookupListener {
+class DefaultStubbingLookupListener implements StubbingLookupListener, Serializable {
+
+ private static final long serialVersionUID = -6789800638070123629L;
private Strictness currentStrictness;
private boolean mismatchesReported;
diff --git a/src/main/java/org/mockito/internal/junit/StrictStubsRunnerTestListener.java b/src/main/java/org/mockito/internal/junit/StrictStubsRunnerTestListener.java
index 9e60c31356..d23431bae1 100644
--- a/src/main/java/org/mockito/internal/junit/StrictStubsRunnerTestListener.java
+++ b/src/main/java/org/mockito/internal/junit/StrictStubsRunnerTestListener.java
@@ -4,7 +4,6 @@
*/
package org.mockito.internal.junit;
-import org.mockito.internal.creation.settings.CreationSettings;
import org.mockito.mock.MockCreationSettings;
import org.mockito.quality.Strictness;
@@ -22,8 +21,8 @@ public void testFinished(TestFinishedEvent event) {}
public void onMockCreated(Object mock, MockCreationSettings settings) {
//It is not ideal that we modify the state of MockCreationSettings object
//MockCreationSettings is intended to be an immutable view of the creation settings
- //In future, we should start passing MockSettings object to the creation listener
- //TODO #793 - when completed, we should be able to get rid of the CreationSettings casting below
- ((CreationSettings) settings).getStubbingLookupListeners().add(stubbingLookupListener);
+ //However, we our previous listeners work this way and it hasn't backfired.
+ //Since it is simple and pragmatic, we'll keep it for now.
+ settings.getStubbingLookupListeners().add(stubbingLookupListener);
}
}
diff --git a/src/main/java/org/mockito/internal/junit/VerificationCollectorImpl.java b/src/main/java/org/mockito/internal/junit/VerificationCollectorImpl.java
index 7c19d35652..e54d47864e 100644
--- a/src/main/java/org/mockito/internal/junit/VerificationCollectorImpl.java
+++ b/src/main/java/org/mockito/internal/junit/VerificationCollectorImpl.java
@@ -75,7 +75,7 @@ private void append(String message) {
this.numberOfFailures++;
this.builder.append('\n')
.append(this.numberOfFailures).append(". ")
- .append(message.substring(1, message.length()));
+ .append(message.trim()).append('\n');
}
private class VerificationWrapper implements VerificationMode {
@@ -89,7 +89,7 @@ private VerificationWrapper(VerificationMode delegate) {
public void verify(VerificationData data) {
try {
this.delegate.verify(data);
- } catch (MockitoAssertionError error) {
+ } catch (AssertionError error) {
VerificationCollectorImpl.this.append(error.getMessage());
}
}
diff --git a/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java b/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java
deleted file mode 100644
index 6fa37a159d..0000000000
--- a/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2017 Mockito contributors
- * This program is made available under the terms of the MIT License.
- */
-package org.mockito.internal.listeners;
-
-/**
- * Listens to attempts to look up stubbing answer for given mocks. This class is internal for now.
- *
- * How does it work?
- * When method is called on the mock object, Mockito looks for any answer (stubbing) declared on that mock.
- * If the stubbed answer is found, that answer is invoked (value returned, thrown exception, etc.).
- * If the answer is not found (e.g. that invocation was not stubbed on the mock), mock's default answer is used.
- * This listener implementation is notified when Mockito looked up an answer for invocation on a mock.
- *
- * If we make this interface a part of public API (and we should):
- * - make the implementation unified with InvocationListener (for example: common parent, marker interface MockObjectListener
- * single method for adding listeners so long they inherit from the parent)
- * - make the error handling strict
- * so that Mockito provides decent message when listener fails due to poor implementation.
- */
-public interface StubbingLookupListener {
-
- /**
- * Called by the framework when Mockito looked up an answer for invocation on a mock.
- *
- * @param stubbingLookupEvent - Information about the looked up stubbing
- */
- void onStubbingLookup(StubbingLookupEvent stubbingLookupEvent);
-}
diff --git a/src/main/java/org/mockito/internal/listeners/StubbingLookupNotifier.java b/src/main/java/org/mockito/internal/listeners/StubbingLookupNotifier.java
index 3e550013a3..a0de0c37b5 100644
--- a/src/main/java/org/mockito/internal/listeners/StubbingLookupNotifier.java
+++ b/src/main/java/org/mockito/internal/listeners/StubbingLookupNotifier.java
@@ -6,6 +6,8 @@
import org.mockito.internal.creation.settings.CreationSettings;
import org.mockito.invocation.Invocation;
+import org.mockito.listeners.StubbingLookupEvent;
+import org.mockito.listeners.StubbingLookupListener;
import org.mockito.mock.MockCreationSettings;
import org.mockito.stubbing.Stubbing;
diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java
new file mode 100644
index 0000000000..979c8f7813
--- /dev/null
+++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.stubbing.defaultanswers;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import org.mockito.internal.MockitoCore;
+import org.mockito.internal.util.MockUtil;
+import org.mockito.internal.util.reflection.GenericMetadataSupport;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.mock.MockCreationSettings;
+
+class RetrieveGenericsForDefaultAnswers {
+
+ private static final MockitoCore MOCKITO_CORE = new MockitoCore();
+
+ static Object returnTypeForMockWithCorrectGenerics(
+ InvocationOnMock invocation, AnswerCallback answerCallback) {
+ Class> type = invocation.getMethod().getReturnType();
+
+ final Type returnType = invocation.getMethod().getGenericReturnType();
+
+ Object defaultReturnValue = null;
+
+ if (returnType instanceof TypeVariable) {
+ type = findTypeFromGeneric(invocation, (TypeVariable) returnType);
+ if (type != null) {
+ defaultReturnValue = delegateChains(type);
+ }
+ }
+
+ if (defaultReturnValue != null) {
+ return defaultReturnValue;
+ }
+
+ if (type != null) {
+ if (!MOCKITO_CORE.isTypeMockable(type)) {
+ return null;
+ }
+
+ return answerCallback.apply(type);
+ }
+
+ return answerCallback.apply(null);
+ }
+
+ /**
+ * Try to resolve the result value using {@link ReturnsEmptyValues} and {@link ReturnsMoreEmptyValues}.
+ *
+ * This will try to use all parent class (superclass & interfaces) to retrieve the value..
+ *
+ * @param type the return type of the method
+ * @return a non-null instance if the type has been resolve. Null otherwise.
+ */
+ private static Object delegateChains(final Class> type) {
+ final ReturnsEmptyValues returnsEmptyValues = new ReturnsEmptyValues();
+ Object result = returnsEmptyValues.returnValueFor(type);
+
+ if (result == null) {
+ Class> emptyValueForClass = type;
+ while (emptyValueForClass != null && result == null) {
+ final Class>[] classes = emptyValueForClass.getInterfaces();
+ for (Class> clazz : classes) {
+ result = returnsEmptyValues.returnValueFor(clazz);
+ if (result != null) {
+ break;
+ }
+ }
+ emptyValueForClass = emptyValueForClass.getSuperclass();
+ }
+ }
+
+ if (result == null) {
+ result = new ReturnsMoreEmptyValues().returnValueFor(type);
+ }
+
+ return result;
+ }
+
+ /**
+ * Retrieve the expected type when it came from a primitive. If the type cannot be retrieve, return null.
+ *
+ * @param invocation the current invocation
+ * @param returnType the expected return type
+ * @return the type or null if not found
+ */
+ private static Class> findTypeFromGeneric(final InvocationOnMock invocation, final TypeVariable returnType) {
+ // Class level
+ final MockCreationSettings mockSettings = MockUtil.getMockHandler(invocation.getMock()).getMockSettings();
+ final GenericMetadataSupport returnTypeSupport = GenericMetadataSupport
+ .inferFrom(mockSettings.getTypeToMock())
+ .resolveGenericReturnType(invocation.getMethod());
+ final Class> rawType = returnTypeSupport.rawType();
+
+ // Method level
+ if (rawType == Object.class) {
+ return findTypeFromGenericInArguments(invocation, returnType);
+ }
+ return rawType;
+ }
+
+ /**
+ * Find a return type using generic arguments provided by the calling method.
+ *
+ * @param invocation the current invocation
+ * @param returnType the expected return type
+ * @return the return type or null if the return type cannot be found
+ */
+ private static Class> findTypeFromGenericInArguments(final InvocationOnMock invocation, final TypeVariable returnType) {
+ final Type[] parameterTypes = invocation.getMethod().getGenericParameterTypes();
+ for (int i = 0; i < parameterTypes.length; i++) {
+ Type argType = parameterTypes[i];
+ if (returnType.equals(argType)) {
+ Object argument = invocation.getArgument(i);
+
+ if (argument == null) {
+ return null;
+ }
+
+ return argument.getClass();
+ }
+ if (argType instanceof GenericArrayType) {
+ argType = ((GenericArrayType) argType).getGenericComponentType();
+ if (returnType.equals(argType)) {
+ return invocation.getArgument(i).getClass();
+ }
+ }
+ }
+ return null;
+ }
+
+ interface AnswerCallback {
+ Object apply(Class> type);
+ }
+}
diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java
index 3909ff041c..356b2e629f 100644
--- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java
+++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java
@@ -25,7 +25,7 @@
/**
* Returning deep stub implementation.
*
- * Will return previously created mock if the invocation matches.
+ *
Will return previously created mock if the invocation matches.
*
*
Supports nested generic information, with this answer you can write code like this :
*
@@ -37,6 +37,8 @@
*
*
*
+ * However this answer does not support generics information when the mock has been deserialized.
+ *
* @see org.mockito.Mockito#RETURNS_DEEP_STUBS
* @see org.mockito.Answers#RETURNS_DEEP_STUBS
*/
@@ -46,13 +48,22 @@ public class ReturnsDeepStubs implements Answer, Serializable {
public Object answer(InvocationOnMock invocation) throws Throwable {
GenericMetadataSupport returnTypeGenericMetadata =
- actualParameterizedType(invocation.getMock()).resolveGenericReturnType(invocation.getMethod());
+ actualParameterizedType(invocation.getMock()).resolveGenericReturnType(invocation.getMethod());
Class> rawType = returnTypeGenericMetadata.rawType();
if (!mockitoCore().isTypeMockable(rawType)) {
return delegate().returnValueFor(rawType);
}
+ // When dealing with erased generics, we only receive the Object type as rawType. At this
+ // point, there is nothing to salvage for Mockito. Instead of trying to be smart and generate
+ // a mock that would potentially match the return signature, instead return `null`. This
+ // is valid per the CheckCast JVM instruction and is better than causing a ClassCastException
+ // on runtime.
+ if (rawType.equals(Object.class) && !returnTypeGenericMetadata.hasRawExtraInterfaces()) {
+ return null;
+ }
+
return deepStub(invocation, returnTypeGenericMetadata);
}
@@ -69,9 +80,9 @@ private Object deepStub(InvocationOnMock invocation, GenericMetadataSupport retu
// record deep stub answer
StubbedInvocationMatcher stubbing = recordDeepStubAnswer(
- newDeepStubMock(returnTypeGenericMetadata, invocation.getMock()),
- container
- );
+ newDeepStubMock(returnTypeGenericMetadata, invocation.getMock()),
+ container
+ );
// deep stubbing creates a stubbing and immediately uses it
// so the stubbing is actually used by the same invocation
@@ -88,24 +99,24 @@ private Object deepStub(InvocationOnMock invocation, GenericMetadataSupport retu
* {@link ReturnsDeepStubs} answer in which we will store the returned type generic metadata.
*
* @param returnTypeGenericMetadata The metadata to use to create the new mock.
- * @param parentMock The parent of the current deep stub mock.
+ * @param parentMock The parent of the current deep stub mock.
* @return The mock
*/
private Object newDeepStubMock(GenericMetadataSupport returnTypeGenericMetadata, Object parentMock) {
MockCreationSettings parentMockSettings = MockUtil.getMockSettings(parentMock);
return mockitoCore().mock(
- returnTypeGenericMetadata.rawType(),
- withSettingsUsing(returnTypeGenericMetadata, parentMockSettings)
- );
+ returnTypeGenericMetadata.rawType(),
+ withSettingsUsing(returnTypeGenericMetadata, parentMockSettings)
+ );
}
private MockSettings withSettingsUsing(GenericMetadataSupport returnTypeGenericMetadata, MockCreationSettings parentMockSettings) {
MockSettings mockSettings = returnTypeGenericMetadata.hasRawExtraInterfaces() ?
- withSettings().extraInterfaces(returnTypeGenericMetadata.rawExtraInterfaces())
- : withSettings();
+ withSettings().extraInterfaces(returnTypeGenericMetadata.rawExtraInterfaces())
+ : withSettings();
return propagateSerializationSettings(mockSettings, parentMockSettings)
- .defaultAnswer(returnsDeepStubsAnswerUsing(returnTypeGenericMetadata));
+ .defaultAnswer(returnsDeepStubsAnswerUsing(returnTypeGenericMetadata));
}
private MockSettings propagateSerializationSettings(MockSettings mockSettings, MockCreationSettings parentMockSettings) {
@@ -139,6 +150,15 @@ public ReturnsDeepStubsSerializationFallback(GenericMetadataSupport returnTypeGe
protected GenericMetadataSupport actualParameterizedType(Object mock) {
return returnTypeGenericMetadata;
}
+
+ /**
+ * Generics support and serialization with deep stubs don't work together.
+ *
+ * The issue is that GenericMetadataSupport is not serializable because
+ * the type elements inferred via reflection are not serializable. Supporting
+ * serialization would require to replace all types coming from the Java reflection
+ * with our own and still managing type equality with the JDK ones.
+ */
private Object writeReplace() throws IOException {
return Mockito.RETURNS_DEEP_STUBS;
}
@@ -152,6 +172,7 @@ private static class DeeplyStubbedAnswer implements Answer, Serializable
DeeplyStubbedAnswer(Object mock) {
this.mock = mock;
}
+
public Object answer(InvocationOnMock invocation) throws Throwable {
return mock;
}
diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java
index 302e4df092..5d4befe60e 100755
--- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java
+++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java
@@ -4,33 +4,36 @@
*/
package org.mockito.internal.stubbing.defaultanswers;
-import org.mockito.internal.MockitoCore;
+import java.io.Serializable;
+import org.mockito.Mockito;
import org.mockito.internal.creation.MockSettingsImpl;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import java.io.Serializable;
-
public class ReturnsMocks implements Answer, Serializable {
private static final long serialVersionUID = -6755257986994634579L;
- private final MockitoCore mockitoCore = new MockitoCore();
private final Answer delegate = new ReturnsMoreEmptyValues();
- public Object answer(InvocationOnMock invocation) throws Throwable {
- Object ret = delegate.answer(invocation);
- if (ret != null) {
- return ret;
- }
-
- return returnValueFor(invocation.getMethod().getReturnType());
- }
+ @Override
+ public Object answer(final InvocationOnMock invocation) throws Throwable {
+ Object defaultReturnValue = delegate.answer(invocation);
- Object returnValueFor(Class> clazz) {
- if (!mockitoCore.isTypeMockable(clazz)) {
- return null;
+ if (defaultReturnValue != null) {
+ return defaultReturnValue;
}
- return mockitoCore.mock(clazz, new MockSettingsImpl().defaultAnswer(this));
+ return RetrieveGenericsForDefaultAnswers.returnTypeForMockWithCorrectGenerics(invocation,
+ new RetrieveGenericsForDefaultAnswers.AnswerCallback() {
+ @Override
+ public Object apply(Class> type) {
+ if (type == null) {
+ type = invocation.getMethod().getReturnType();
+ }
+
+ return Mockito
+ .mock(type, new MockSettingsImpl().defaultAnswer(ReturnsMocks.this));
+ }
+ });
}
}
diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java
index ff6b7942d1..6cf50c7d39 100644
--- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java
+++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java
@@ -8,18 +8,10 @@
import static org.mockito.internal.util.ObjectMethodsGuru.isToStringMethod;
import java.io.Serializable;
-import java.lang.reflect.GenericArrayType;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Type;
-import java.lang.reflect.TypeVariable;
-
import org.mockito.Mockito;
import org.mockito.internal.debugging.LocationImpl;
-import org.mockito.internal.util.MockUtil;
-import org.mockito.internal.util.reflection.GenericMetadataSupport;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.invocation.Location;
-import org.mockito.mock.MockCreationSettings;
import org.mockito.stubbing.Answer;
/**
@@ -46,108 +38,25 @@ public class ReturnsSmartNulls implements Answer, Serializable {
private final Answer delegate = new ReturnsMoreEmptyValues();
+ @Override
public Object answer(final InvocationOnMock invocation) throws Throwable {
Object defaultReturnValue = delegate.answer(invocation);
- if (defaultReturnValue != null) {
- return defaultReturnValue;
- }
- Class> type = invocation.getMethod().getReturnType();
- final Type returnType = invocation.getMethod().getGenericReturnType();
- if (returnType instanceof TypeVariable) {
- type = findTypeFromGeneric(invocation, (TypeVariable) returnType);
- if (type != null) {
- defaultReturnValue = delegateChains(type);
- }
- }
if (defaultReturnValue != null) {
return defaultReturnValue;
}
- if (type != null && !type.isPrimitive() && !Modifier.isFinal(type.getModifiers())) {
- final Location location = new LocationImpl();
- return Mockito.mock(type, new ThrowsSmartNullPointer(invocation, location));
- }
- return null;
- }
-
- /**
- * Try to resolve the result value using {@link ReturnsEmptyValues} and {@link ReturnsMoreEmptyValues}.
- *
- * This will try to use all parent class (superclass & interfaces) to retrieve the value..
- *
- * @param type the return type of the method
- * @return a non-null instance if the type has been resolve. Null otherwise.
- */
- private Object delegateChains(final Class> type) {
- final ReturnsEmptyValues returnsEmptyValues = new ReturnsEmptyValues();
- Object result = returnsEmptyValues.returnValueFor(type);
-
- if (result == null) {
- Class> emptyValueForClass = type;
- while (emptyValueForClass != null && result == null) {
- final Class>[] classes = emptyValueForClass.getInterfaces();
- for (Class> clazz : classes) {
- result = returnsEmptyValues.returnValueFor(clazz);
- if (result != null) {
- break;
+ return RetrieveGenericsForDefaultAnswers.returnTypeForMockWithCorrectGenerics(invocation,
+ new RetrieveGenericsForDefaultAnswers.AnswerCallback() {
+ @Override
+ public Object apply(Class> type) {
+ if (type == null) {
+ return null;
}
- }
- emptyValueForClass = emptyValueForClass.getSuperclass();
- }
- }
-
- if (result == null) {
- result = new ReturnsMoreEmptyValues().returnValueFor(type);
- }
-
- return result;
- }
-
- /**
- * Retrieve the expected type when it came from a primitive. If the type cannot be retrieve, return null.
- *
- * @param invocation the current invocation
- * @param returnType the expected return type
- * @return the type or null if not found
- */
- private Class> findTypeFromGeneric(final InvocationOnMock invocation, final TypeVariable returnType) {
- // Class level
- final MockCreationSettings mockSettings = MockUtil.getMockHandler(invocation.getMock()).getMockSettings();
- final GenericMetadataSupport returnTypeSupport = GenericMetadataSupport
- .inferFrom(mockSettings.getTypeToMock())
- .resolveGenericReturnType(invocation.getMethod());
- final Class> rawType = returnTypeSupport.rawType();
- // Method level
- if (rawType == Object.class) {
- return findTypeFromGenericInArguments(invocation, returnType);
- }
- return rawType;
- }
-
- /**
- * Find a return type using generic arguments provided by the calling method.
- *
- * @param invocation the current invocation
- * @param returnType the expected return type
- * @return the return type or null if the return type cannot be found
- */
- private Class> findTypeFromGenericInArguments(final InvocationOnMock invocation, final TypeVariable returnType) {
- final Type[] parameterTypes = invocation.getMethod().getGenericParameterTypes();
- for (int i = 0; i < parameterTypes.length; i++) {
- Type argType = parameterTypes[i];
- if (returnType.equals(argType)) {
- return invocation.getArgument(i).getClass();
- }
- if (argType instanceof GenericArrayType) {
- argType = ((GenericArrayType) argType).getGenericComponentType();
- if (returnType.equals(argType)) {
- return invocation.getArgument(i).getClass();
+ return Mockito.mock(type, new ThrowsSmartNullPointer(invocation, new LocationImpl()));
}
- }
- }
- return null;
+ });
}
private static class ThrowsSmartNullPointer implements Answer {
@@ -156,7 +65,7 @@ private static class ThrowsSmartNullPointer implements Answer {
private final Location location;
- public ThrowsSmartNullPointer(InvocationOnMock unstubbedInvocation, Location location) {
+ ThrowsSmartNullPointer(InvocationOnMock unstubbedInvocation, Location location) {
this.unstubbedInvocation = unstubbedInvocation;
this.location = location;
}
diff --git a/src/main/java/org/mockito/internal/util/reflection/GenericMetadataSupport.java b/src/main/java/org/mockito/internal/util/reflection/GenericMetadataSupport.java
index 596827c64c..80cbf65be3 100644
--- a/src/main/java/org/mockito/internal/util/reflection/GenericMetadataSupport.java
+++ b/src/main/java/org/mockito/internal/util/reflection/GenericMetadataSupport.java
@@ -8,8 +8,23 @@
import org.mockito.exceptions.base.MockitoException;
import org.mockito.internal.util.Checks;
-import java.lang.reflect.*;
-import java.util.*;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
/**
@@ -17,20 +32,20 @@
* and accessible members.
*
*
- * The main idea of this code is to create a Map that will help to resolve return types.
- * In order to actually work with nested generics, this map will have to be passed along new instances
- * as a type context.
+ * The main idea of this code is to create a Map that will help to resolve return types.
+ * In order to actually work with nested generics, this map will have to be passed along new instances
+ * as a type context.
*
*
*
- * Hence :
- *
- * A new instance representing the metadata is created using the {@link #inferFrom(Type)} method from a real
- * Class
or from a ParameterizedType
, other types are not yet supported.
+ * Hence :
+ *
+ * A new instance representing the metadata is created using the {@link #inferFrom(Type)} method from a real
+ * Class
or from a ParameterizedType
, other types are not yet supported.
*
- * Then from this metadata, we can extract meta-data for a generic return type of a method, using
- * {@link #resolveGenericReturnType(Method)}.
- *
+ * Then from this metadata, we can extract meta-data for a generic return type of a method, using
+ * {@link #resolveGenericReturnType(Method)}.
+ *
*
*
*
@@ -97,8 +112,9 @@ protected Class> extractRawTypeOf(Type type) {
}
if (type instanceof TypeVariable) {
/*
- * If type is a TypeVariable, then it is needed to gather data elsewhere. Usually TypeVariables are declared
- * on the class definition, such as such as List.
+ * If type is a TypeVariable, then it is needed to gather data elsewhere.
+ * Usually TypeVariables are declared on the class definition, such as such
+ * as List.
*/
return extractRawTypeOf(contextualActualTypeParameters.get(type));
}
@@ -116,10 +132,21 @@ protected void registerTypeVariablesOn(Type classType) {
TypeVariable> typeParameter = typeParameters[i];
Type actualTypeArgument = actualTypeArguments[i];
- // Prevent registration of a cycle of TypeVariables. This can happen when we are processing
- // type parameters in a Method, while we already processed the type parameters of a class.
- if (actualTypeArgument instanceof TypeVariable && contextualActualTypeParameters.containsKey(typeParameter)) {
- continue;
+ if (actualTypeArgument instanceof TypeVariable) {
+ /*
+ * If actualTypeArgument is a TypeVariable, and it is not present in
+ * the context map then it is needed to try harder to gather more data
+ * from the type argument itself. In some case the type argument do
+ * define upper bounds, this allow to look for them if not in the
+ * context map.
+ */
+ registerTypeVariableIfNotPresent((TypeVariable>) actualTypeArgument);
+
+ // Prevent registration of a cycle of TypeVariables. This can happen when we are processing
+ // type parameters in a Method, while we already processed the type parameters of a class.
+ if (contextualActualTypeParameters.containsKey(typeParameter)) {
+ continue;
+ }
}
if (actualTypeArgument instanceof WildcardType) {
@@ -147,7 +174,7 @@ private void registerTypeVariableIfNotPresent(TypeVariable> typeVariable) {
/**
* @param typeParameter The TypeVariable parameter
* @return A {@link BoundedType} for easy bound information, if first bound is a TypeVariable
- * then retrieve BoundedType of this TypeVariable
+ * then retrieve BoundedType of this TypeVariable
*/
private BoundedType boundsOf(TypeVariable> typeParameter) {
if (typeParameter.getBounds()[0] instanceof TypeVariable) {
@@ -159,13 +186,17 @@ private BoundedType boundsOf(TypeVariable> typeParameter) {
/**
* @param wildCard The WildCard type
* @return A {@link BoundedType} for easy bound information, if first bound is a TypeVariable
- * then retrieve BoundedType of this TypeVariable
+ * then retrieve BoundedType of this TypeVariable
*/
private BoundedType boundsOf(WildcardType wildCard) {
/*
* According to JLS(http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1):
- * - Lower and upper can't coexist: (for instance, this is not allowed: extends List & super MyInterface>)
- * - Multiple bounds are not supported (for instance, this is not allowed: extends List & MyInterface>)
+ * - Lower and upper can't coexist: (for instance, this is not allowed:
+ * extends List & super MyInterface>)
+ * - Multiple concrete type bounds are not supported (for instance, this is not allowed:
+ * extends ArrayList & MyInterface>)
+ * But the following form is possible where there is a single concrete tyep bound followed by interface type bounds
+ * & Comparable>
*/
WildCardBoundedType wildCardBoundedType = new WildCardBoundedType(wildCard);
@@ -241,7 +272,7 @@ public GenericMetadataSupport resolveGenericReturnType(Method method) {
// logger.log("Method '" + method.toGenericString() + "' has return type : " + genericReturnType.getClass().getInterfaces()[0].getSimpleName() + " : " + genericReturnType);
int arity = 0;
- while(genericReturnType instanceof GenericArrayType) {
+ while (genericReturnType instanceof GenericArrayType) {
arity++;
genericReturnType = ((GenericArrayType) genericReturnType).getGenericComponentType();
}
@@ -273,8 +304,8 @@ private GenericMetadataSupport resolveGenericType(Type type, Method method) {
* Create an new instance of {@link GenericMetadataSupport} inferred from a {@link Type}.
*
*
- * At the moment type
can only be a {@link Class} or a {@link ParameterizedType}, otherwise
- * it'll throw a {@link MockitoException}.
+ * At the moment type
can only be a {@link Class} or a {@link ParameterizedType}, otherwise
+ * it'll throw a {@link MockitoException}.
*
*
* @param type The class from which the {@link GenericMetadataSupport} should be built.
@@ -300,7 +331,7 @@ public static GenericMetadataSupport inferFrom(Type type) {
/**
* Generic metadata implementation for {@link Class}.
- *
+ *
* Offer support to retrieve generic metadata on a {@link Class} by reading type parameters and type variables on
* the class and its ancestors and interfaces.
*/
@@ -322,10 +353,10 @@ public Class> rawType() {
/**
* Generic metadata implementation for "standalone" {@link ParameterizedType}.
- *
+ *
* Offer support to retrieve generic metadata on a {@link ParameterizedType} by reading type variables of
* the related raw type and declared type variable of this parameterized type.
- *
+ *
* This class is not designed to work on ParameterizedType returned by {@link Method#getGenericReturnType()}, as
* the ParameterizedType instance return in these cases could have Type Variables that refer to type declaration(s).
* That's what meant the "standalone" word at the beginning of the Javadoc.
@@ -387,6 +418,7 @@ private static class TypeVariableReturnType extends GenericMetadataSupport {
private final TypeVariable> typeVariable;
private final TypeVariable>[] typeParameters;
private Class> rawType;
+ private List extraInterfaces;
public TypeVariableReturnType(GenericMetadataSupport source, TypeVariable>[] typeParameters, TypeVariable> typeVariable) {
this.typeParameters = typeParameters;
@@ -405,7 +437,7 @@ private void readTypeVariables() {
for (Type type : typeVariable.getBounds()) {
registerTypeVariablesOn(type);
}
- registerTypeParametersOn(new TypeVariable[] { typeVariable });
+ registerTypeParametersOn(new TypeVariable[]{typeVariable});
registerTypeVariablesOn(getActualTypeArgumentFor(typeVariable));
}
@@ -419,15 +451,18 @@ public Class> rawType() {
@Override
public List extraInterfaces() {
+ if (extraInterfaces != null) {
+ return extraInterfaces;
+ }
Type type = extractActualBoundedTypeOf(typeVariable);
if (type instanceof BoundedType) {
- return Arrays.asList(((BoundedType) type).interfaceBounds());
+ return extraInterfaces = Arrays.asList(((BoundedType) type).interfaceBounds());
}
if (type instanceof ParameterizedType) {
- return Collections.singletonList(type);
+ return extraInterfaces = Collections.singletonList(type);
}
if (type instanceof Class) {
- return Collections.emptyList();
+ return extraInterfaces = Collections.emptyList();
}
throw new MockitoException("Cannot extract extra-interfaces from '" + typeVariable + "' : '" + type + "'");
}
@@ -442,7 +477,7 @@ public Class>[] rawExtraInterfaces() {
for (Type extraInterface : extraInterfaces) {
Class> rawInterface = extractRawTypeOf(extraInterface);
// avoid interface collision with actual raw type (with typevariables, resolution ca be quite aggressive)
- if(!rawType().equals(rawInterface)) {
+ if (!rawType().equals(rawInterface)) {
rawExtraInterfaces.add(rawInterface);
}
}
@@ -514,7 +549,6 @@ public Class> rawType() {
}
-
/**
* Type representing bounds of a type
*
@@ -537,7 +571,7 @@ public interface BoundedType extends Type {
*
* If upper bounds are declared with SomeClass and additional interfaces, then firstBound will be SomeClass and
* interfacesBound will be an array of the additional interfaces.
- *
+ *
* i.e. SomeClass
.
*
* interface UpperBoundedTypeWithClass & Cloneable> {
diff --git a/src/main/java/org/mockito/invocation/Location.java b/src/main/java/org/mockito/invocation/Location.java
index 9337668283..43b48324d8 100644
--- a/src/main/java/org/mockito/invocation/Location.java
+++ b/src/main/java/org/mockito/invocation/Location.java
@@ -23,7 +23,7 @@ public interface Location {
* Source file of this location
*
* @return source file
- * @since 2.23.5
+ * @since 2.24.6
*/
String getSourceFile();
}
diff --git a/src/main/java/org/mockito/internal/listeners/StubbingLookupEvent.java b/src/main/java/org/mockito/listeners/StubbingLookupEvent.java
similarity index 87%
rename from src/main/java/org/mockito/internal/listeners/StubbingLookupEvent.java
rename to src/main/java/org/mockito/listeners/StubbingLookupEvent.java
index 0d45edd345..a03fb594ea 100644
--- a/src/main/java/org/mockito/internal/listeners/StubbingLookupEvent.java
+++ b/src/main/java/org/mockito/listeners/StubbingLookupEvent.java
@@ -2,7 +2,7 @@
* Copyright (c) 2018 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
-package org.mockito.internal.listeners;
+package org.mockito.listeners;
import org.mockito.invocation.Invocation;
import org.mockito.mock.MockCreationSettings;
@@ -12,25 +12,32 @@
/**
* Represent an information about the looked up stubbing
+ *
+ * @since 2.24.6
*/
public interface StubbingLookupEvent {
+
/**
* @return The invocation that causes stubbing lookup
+ * @since 2.24.6
*/
Invocation getInvocation();
/**
* @return Looked up stubbing. It can be null
, which indicates that the invocation was not stubbed
+ * @since 2.24.6
*/
Stubbing getStubbingFound();
/**
* @return All stubbings declared on the mock object that we are invoking
+ * @since 2.24.6
*/
Collection getAllStubbings();
/**
* @return Settings of the mock object that we are invoking
+ * @since 2.24.6
*/
MockCreationSettings getMockSettings();
}
diff --git a/src/main/java/org/mockito/listeners/StubbingLookupListener.java b/src/main/java/org/mockito/listeners/StubbingLookupListener.java
new file mode 100644
index 0000000000..b33e063462
--- /dev/null
+++ b/src/main/java/org/mockito/listeners/StubbingLookupListener.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2017 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.listeners;
+
+import org.mockito.MockSettings;
+import org.mockito.mock.MockCreationSettings;
+
+/**
+ * When a method is called on a mock object Mockito looks up any stubbings recorded on that mock.
+ * This listener gets notified on stubbing lookup.
+ * Register listener via {@link MockSettings#stubbingLookupListeners(StubbingLookupListener...)}.
+ * This API is used by Mockito to implement {@link org.mockito.exceptions.misusing.PotentialStubbingProblem}
+ * (part of Mockito {@link org.mockito.quality.Strictness}).
+ *
+ * Details: When method is called on the mock object, Mockito looks for any answer (stubbing) declared on that mock.
+ * If the stubbed answer is found, that answer is then invoked (value returned, thrown exception, etc.).
+ * If the answer is not found (e.g. that invocation was not stubbed on the mock), mock's default answer is used.
+ * This listener implementation is notified when Mockito attempts to find an answer for invocation on a mock.
+ *
+ * The listeners can be accessed via {@link MockCreationSettings#getStubbingLookupListeners()}.
+ *
+ * @since 2.24.6
+ */
+public interface StubbingLookupListener {
+
+ /**
+ * Called by the framework when Mockito looked up an answer for invocation on a mock.
+ * For details, see {@link StubbingLookupListener}.
+ *
+ * @param stubbingLookupEvent - Information about the looked up stubbing
+ * @since 2.24.6
+ */
+ void onStubbingLookup(StubbingLookupEvent stubbingLookupEvent);
+}
diff --git a/src/main/java/org/mockito/mock/MockCreationSettings.java b/src/main/java/org/mockito/mock/MockCreationSettings.java
index 7e74be8a90..2343b0da64 100644
--- a/src/main/java/org/mockito/mock/MockCreationSettings.java
+++ b/src/main/java/org/mockito/mock/MockCreationSettings.java
@@ -9,6 +9,7 @@
import org.mockito.MockSettings;
import org.mockito.NotExtensible;
import org.mockito.listeners.InvocationListener;
+import org.mockito.listeners.StubbingLookupListener;
import org.mockito.listeners.VerificationStartedListener;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
@@ -70,7 +71,17 @@ public interface MockCreationSettings {
boolean isStripAnnotations();
/**
- * {@link InvocationListener} instances attached to this mock, see {@link org.mockito.MockSettings#invocationListeners}.
+ * Returns {@link StubbingLookupListener} instances attached to this mock via {@link MockSettings#stubbingLookupListeners(StubbingLookupListener...)}.
+ * The resulting list is mutable, you can add/remove listeners even after the mock was created.
+ *
+ * For more details see {@link StubbingLookupListener}.
+ *
+ * @since 2.24.6
+ */
+ List getStubbingLookupListeners();
+
+ /**
+ * {@link InvocationListener} instances attached to this mock, see {@link org.mockito.MockSettings#invocationListeners(InvocationListener...)}.
*/
List getInvocationListeners();
diff --git a/src/test/java/org/mockito/internal/creation/MockSettingsImplTest.java b/src/test/java/org/mockito/internal/creation/MockSettingsImplTest.java
index 18911e4ddf..63cb1e7218 100644
--- a/src/test/java/org/mockito/internal/creation/MockSettingsImplTest.java
+++ b/src/test/java/org/mockito/internal/creation/MockSettingsImplTest.java
@@ -5,25 +5,31 @@
package org.mockito.internal.creation;
import org.assertj.core.api.Assertions;
+import org.assertj.core.api.ThrowableAssert;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.internal.debugging.VerboseMockInvocationLogger;
import org.mockito.listeners.InvocationListener;
+import org.mockito.listeners.StubbingLookupListener;
import org.mockitoutil.TestBase;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
-import static org.junit.Assert.*;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
public class MockSettingsImplTest extends TestBase {
private MockSettingsImpl> mockSettingsImpl = new MockSettingsImpl();
@Mock private InvocationListener invocationListener;
+ @Mock private StubbingLookupListener stubbingLookupListener;
@Test(expected=MockitoException.class)
@SuppressWarnings("unchecked")
@@ -112,12 +118,6 @@ public void shouldAddVerboseLoggingListenerOnlyOnce() {
Assertions.assertThat(mockSettingsImpl.getInvocationListeners()).hasSize(1);
}
- @SuppressWarnings("unchecked")
- @Test(expected=MockitoException.class)
- public void shouldNotAllowNullListener() {
- mockSettingsImpl.invocationListeners((InvocationListener[])null);
- }
-
@Test
@SuppressWarnings("unchecked")
public void shouldAddInvocationListener() {
@@ -145,22 +145,98 @@ public void canAddDuplicateInvocationListeners_ItsNotOurBusinessThere() {
}
@Test
- public void shouldReportErrorWhenAddingNoInvocationListeners() throws Exception {
- try {
- mockSettingsImpl.invocationListeners();
- fail();
- } catch (Exception e) {
- Assertions.assertThat(e.getMessage()).contains("at least one listener");
- }
+ public void validates_listeners() {
+ assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
+ public void call() {
+ mockSettingsImpl.addListeners(new Object[] {}, new LinkedList(), "myListeners");
+ }
+ }).hasMessageContaining("myListeners() requires at least one listener");
+
+ assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
+ public void call() {
+ mockSettingsImpl.addListeners(null, new LinkedList(), "myListeners");
+ }
+ }).hasMessageContaining("myListeners() does not accept null vararg array");
+
+ assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
+ public void call() {
+ mockSettingsImpl.addListeners(new Object[] {null}, new LinkedList(), "myListeners");
+ }
+ }).hasMessageContaining("myListeners() does not accept null listeners");
+ }
+
+
+ @Test
+ public void validates_stubbing_lookup_listeners() {
+ assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
+ public void call() {
+ mockSettingsImpl.stubbingLookupListeners(new StubbingLookupListener[] {});
+ }
+ }).hasMessageContaining("stubbingLookupListeners() requires at least one listener");
+
+ assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
+ public void call() {
+ mockSettingsImpl.stubbingLookupListeners(null);
+ }
+ }).hasMessageContaining("stubbingLookupListeners() does not accept null vararg array");
+
+ assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
+ public void call() {
+ mockSettingsImpl.stubbingLookupListeners(new StubbingLookupListener[] {null});
+ }
+ }).hasMessageContaining("stubbingLookupListeners() does not accept null listeners");
+ }
+
+ @Test
+ public void validates_invocation_listeners() {
+ assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
+ public void call() {
+ mockSettingsImpl.invocationListeners(new InvocationListener[] {});
+ }
+ }).hasMessageContaining("invocationListeners() requires at least one listener");
+
+ assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
+ public void call() {
+ mockSettingsImpl.invocationListeners(null);
+ }
+ }).hasMessageContaining("invocationListeners() does not accept null vararg array");
+
+ assertThatThrownBy(new ThrowableAssert.ThrowingCallable() {
+ public void call() {
+ mockSettingsImpl.invocationListeners(new InvocationListener[] {null});
+ }
+ }).hasMessageContaining("invocationListeners() does not accept null listeners");
+ }
+
+ @Test
+ public void addListeners_has_empty_listeners_by_default() {
+ assertTrue(mockSettingsImpl.getInvocationListeners().isEmpty());
+ assertTrue(mockSettingsImpl.getStubbingLookupListeners().isEmpty());
+ }
+
+ @Test
+ public void addListeners_shouldAddMockObjectListeners() {
+ //when
+ mockSettingsImpl.invocationListeners(invocationListener);
+ mockSettingsImpl.stubbingLookupListeners(stubbingLookupListener);
+
+ //then
+ assertThat(mockSettingsImpl.getInvocationListeners()).contains(invocationListener);
+ assertThat(mockSettingsImpl.getStubbingLookupListeners()).contains(stubbingLookupListener);
}
@Test
- public void shouldReportErrorWhenAddingANullInvocationListener() throws Exception {
- try {
- mockSettingsImpl.invocationListeners(invocationListener, null);
- fail();
- } catch (Exception e) {
- Assertions.assertThat(e.getMessage()).contains("does not accept null");
- }
+ public void addListeners_canAddDuplicateMockObjectListeners_ItsNotOurBusinessThere() {
+ //when
+ mockSettingsImpl.stubbingLookupListeners(stubbingLookupListener)
+ .stubbingLookupListeners(stubbingLookupListener)
+ .invocationListeners(invocationListener)
+ .invocationListeners(invocationListener);
+
+ //then
+ assertThat(mockSettingsImpl.getInvocationListeners())
+ .containsSequence(invocationListener, invocationListener);
+ assertThat(mockSettingsImpl.getStubbingLookupListeners())
+ .containsSequence(stubbingLookupListener, stubbingLookupListener);
}
}
diff --git a/src/test/java/org/mockito/internal/listeners/StubbingLookupNotifierTest.java b/src/test/java/org/mockito/internal/listeners/StubbingLookupNotifierTest.java
index ac963c4855..40e5396920 100644
--- a/src/test/java/org/mockito/internal/listeners/StubbingLookupNotifierTest.java
+++ b/src/test/java/org/mockito/internal/listeners/StubbingLookupNotifierTest.java
@@ -9,6 +9,7 @@
import org.mockito.ArgumentMatcher;
import org.mockito.internal.creation.settings.CreationSettings;
import org.mockito.invocation.Invocation;
+import org.mockito.listeners.StubbingLookupListener;
import org.mockito.stubbing.Stubbing;
import org.mockitoutil.TestBase;
diff --git a/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsGenericDeepStubsTest.java b/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsGenericDeepStubsTest.java
index d91df8c40e..b051e124a8 100644
--- a/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsGenericDeepStubsTest.java
+++ b/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsGenericDeepStubsTest.java
@@ -5,39 +5,52 @@
package org.mockito.internal.stubbing.defaultanswers;
import org.junit.Test;
+import org.mockitousage.examples.use.Article;
+import java.io.Closeable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Callable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
@SuppressWarnings("unused")
public class ReturnsGenericDeepStubsTest {
- interface ListOfInteger extends List {}
+ interface ListOfInteger extends List {
+ }
- interface AnotherListOfInteger extends ListOfInteger {}
+ interface AnotherListOfInteger extends ListOfInteger {
+ }
interface GenericsNest & Cloneable> extends Map> {
Set remove(Object key); // override with fixed ParameterizedType
+
List super Number> returningWildcard();
+
Map returningNonMockableNestedGeneric();
+
K returningK();
+
List paramTypeWithTypeParams();
+
T twoTypeParams(S s);
+
O typeVarWithTypeParams();
+
Number returnsNormalType();
}
@Test
- public void generic_deep_mock_frenzy__look_at_these_chained_calls() throws Exception {
+ public void generic_deep_mock_frenzy__look_at_these_chained_calls() {
GenericsNest> mock = mock(GenericsNest.class, RETURNS_DEEP_STUBS);
Set extends Map.Entry extends Cloneable, Set>> entries = mock.entrySet();
- Iterator extends Map.Entry extends Cloneable,Set>> entriesIterator = mock.entrySet().iterator();
+ Iterator extends Map.Entry extends Cloneable, Set>> entriesIterator = mock.entrySet().iterator();
Map.Entry extends Cloneable, Set> nextEntry = mock.entrySet().iterator().next();
Cloneable cloneableKey = mock.entrySet().iterator().next().getKey();
@@ -49,17 +62,17 @@ public void generic_deep_mock_frenzy__look_at_these_chained_calls() throws Excep
}
@Test
- public void can_create_mock_from_multiple_type_variable_bounds_when_return_type_of_parameterized_method_is_a_parameterizedtype_that_is_referencing_a_typevar_on_class() throws Exception {
+ public void can_create_mock_from_multiple_type_variable_bounds_when_return_type_of_parameterized_method_is_a_parameterizedType_that_is_referencing_a_typeVar_on_class() {
GenericsNest> mock = mock(GenericsNest.class, RETURNS_DEEP_STUBS);
Cloneable cloneable_bound_that_is_declared_on_typevar_K_in_the_class_which_is_referenced_by_typevar_O_declared_on_the_method =
- mock.paramTypeWithTypeParams().get(0);
+ mock.paramTypeWithTypeParams().get(0);
Comparable> comparable_bound_that_is_declared_on_typevar_K_in_the_class_which_is_referenced_by_typevar_O_declared_on_the_method =
- mock.paramTypeWithTypeParams().get(0);
+ mock.paramTypeWithTypeParams().get(0);
}
@Test
- public void can_create_mock_from_multiple_type_variable_bounds_when_method_return_type_is_referencing_a_typevar_on_class() throws Exception {
+ public void can_create_mock_from_multiple_type_variable_bounds_when_method_return_type_is_referencing_a_typeVar_on_class() {
GenericsNest> mock = mock(GenericsNest.class, RETURNS_DEEP_STUBS);
Cloneable cloneable_bound_of_typevar_K = mock.returningK();
@@ -67,7 +80,7 @@ public void can_create_mock_from_multiple_type_variable_bounds_when_method_retur
}
@Test
- public void can_create_mock_from_multiple_type_variable_bounds_when_return_type_of_parameterized_method_is_a_typevar_that_is_referencing_a_typevar_on_class() throws Exception {
+ public void can_create_mock_from_multiple_type_variable_bounds_when_return_type_of_parameterized_method_is_a_typeVar_that_is_referencing_a_typeVar_on_class() {
GenericsNest> mock = mock(GenericsNest.class, RETURNS_DEEP_STUBS);
Cloneable cloneable_bound_of_typevar_K_referenced_by_typevar_O = (Cloneable) mock.typeVarWithTypeParams();
@@ -75,7 +88,7 @@ public void can_create_mock_from_multiple_type_variable_bounds_when_return_type_
}
@Test
- public void can_create_mock_from_return_types_declared_with_a_bounded_wildcard() throws Exception {
+ public void can_create_mock_from_return_types_declared_with_a_bounded_wildcard() {
GenericsNest> mock = mock(GenericsNest.class, RETURNS_DEEP_STUBS);
List super Integer> objects = mock.returningWildcard();
@@ -84,7 +97,7 @@ public void can_create_mock_from_return_types_declared_with_a_bounded_wildcard()
}
@Test
- public void can_still_work_with_raw_type_in_the_return_type() throws Exception {
+ public void can_still_work_with_raw_type_in_the_return_type() {
GenericsNest> mock = mock(GenericsNest.class, RETURNS_DEEP_STUBS);
Number the_raw_type_that_should_be_returned = mock.returnsNormalType();
@@ -92,7 +105,7 @@ public void can_still_work_with_raw_type_in_the_return_type() throws Exception {
}
@Test
- public void will_return_default_value_on_non_mockable_nested_generic() throws Exception {
+ public void will_return_default_value_on_non_mockable_nested_generic() {
GenericsNest> genericsNest = mock(GenericsNest.class, RETURNS_DEEP_STUBS);
ListOfInteger listOfInteger = mock(ListOfInteger.class, RETURNS_DEEP_STUBS);
AnotherListOfInteger anotherListOfInteger = mock(AnotherListOfInteger.class, RETURNS_DEEP_STUBS);
@@ -103,11 +116,80 @@ public void will_return_default_value_on_non_mockable_nested_generic() throws Ex
}
@Test(expected = ClassCastException.class)
- public void as_expected_fail_with_a_CCE_on_callsite_when_erasure_takes_place_for_example___StringBuilder_is_subject_to_erasure() throws Exception {
+ public void as_expected_fail_with_a_CCE_on_call_site_when_erasure_takes_place_for_example___StringBuilder_is_subject_to_erasure() {
GenericsNest> mock = mock(GenericsNest.class, RETURNS_DEEP_STUBS);
// following assignment needed to create a ClassCastException on the call site (i.e. : here)
StringBuilder stringBuilder_assignment_that_should_throw_a_CCE =
- mock.twoTypeParams(new StringBuilder()).append(2).append(3);
+ mock.twoTypeParams(new StringBuilder()).append(2).append(3);
+ }
+
+ class WithGenerics {
+ T execute() {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ class SubClass extends WithGenerics {
+ }
+
+ class UserOfSubClass {
+ SubClass generate() {
+ return null;
+ }
+ }
+
+ @Test
+ public void can_handle_deep_stubs_with_generics_at_end_of_deep_invocation() {
+ UserOfSubClass mock = mock(UserOfSubClass.class, RETURNS_DEEP_STUBS);
+
+ when(mock.generate().execute()).thenReturn("sub");
+
+ assertThat(mock.generate().execute()).isEqualTo("sub");
+ }
+
+ public interface TopInterface {
+ T generic();
+ }
+
+ public interface MiddleInterface extends TopInterface {
+ }
+
+ public class OwningClassWithDeclaredUpperBounds & Callable & Closeable> {
+ public abstract class AbstractInner implements MiddleInterface {
+ }
+ }
+
+ @Test
+ public void cannot_handle_deep_stubs_with_generics_declared_upper_bounds_at_end_of_deep_invocation() throws Exception {
+ OwningClassWithDeclaredUpperBounds.AbstractInner mock =
+ mock(OwningClassWithDeclaredUpperBounds.AbstractInner.class, RETURNS_DEEP_STUBS);
+
+ // It seems that while the syntax used on OwningClassWithDeclaredUpperBounds.AbstractInner
+ // appear to be legal, the javac compiler does not follow through
+ // hence we need casting, this may also explain why GenericMetadataSupport has trouble to
+ // extract matching data as well.
+
+ assertThat(mock.generic())
+ .describedAs("mock should implement first bound : 'Iterable'")
+ .isInstanceOf(Iterable.class);
+ assertThat(((Iterable) mock.generic()).iterator())
+ .describedAs("Iterable returns Iterator").isInstanceOf(Iterator.class);
+ assertThat(((Iterable) mock.generic()).iterator().next())
+ .describedAs("Cannot yet extract Type argument 'Article' so return null instead of a mock "
+ + "of type Object (which would raise CCE on the call-site)")
+ .isNull();
+
+ assertThat(mock.generic())
+ .describedAs("mock should implement second interface bound : 'Callable'")
+ .isInstanceOf(Callable.class);
+ assertThat(((Callable) mock.generic()).call())
+ .describedAs("Cannot yet extract Type argument 'Article' so return null instead of a mock "
+ + "of type Object (which would raise CCE on the call-site)")
+ .isNull();
+
+ assertThat(mock.generic())
+ .describedAs("mock should implement third interface bound : 'Closeable'")
+ .isInstanceOf(Closeable.class);
}
}
diff --git a/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocksTest.java b/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocksTest.java
index 7de160c09e..ba5b822d30 100755
--- a/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocksTest.java
+++ b/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocksTest.java
@@ -6,15 +6,24 @@
import org.junit.Test;
import org.mockito.internal.configuration.plugins.Plugins;
+import org.mockito.internal.stubbing.defaultanswers.ReturnsGenericDeepStubsTest.WithGenerics;
import org.mockito.internal.util.MockUtil;
import org.mockitoutil.TestBase;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeFalse;
+import static org.mockito.Mockito.when;
public class ReturnsMocksTest extends TestBase {
private ReturnsMocks values = new ReturnsMocks();
+ interface AllInterface {
+ FooInterface getInterface();
+ BarClass getNormalClass();
+ Baz getFinalClass();
+ WithGenerics withGenerics();
+ }
+
interface FooInterface {
}
@@ -25,21 +34,30 @@ final class Baz {
}
@Test
- public void should_return_mock_value_for_interface() throws Exception {
- Object interfaceMock = values.returnValueFor(FooInterface.class);
+ public void should_return_mock_value_for_interface() throws Throwable {
+ Object interfaceMock = values.answer(invocationOf(AllInterface.class, "getInterface"));
assertTrue(MockUtil.isMock(interfaceMock));
}
@Test
- public void should_return_mock_value_for_class() throws Exception {
- Object classMock = values.returnValueFor(BarClass.class);
+ public void should_return_mock_value_for_class() throws Throwable {
+ Object classMock = values.answer(invocationOf(AllInterface.class, "getNormalClass"));
+ assertTrue(MockUtil.isMock(classMock));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void should_return_mock_value_for_generic_class() throws Throwable {
+ WithGenerics classMock = (WithGenerics) values.answer(invocationOf(AllInterface.class, "withGenerics"));
assertTrue(MockUtil.isMock(classMock));
+ when(classMock.execute()).thenReturn("return");
+ assertEquals("return", classMock.execute());
}
@Test
- public void should_return_null_for_final_class_if_unsupported() throws Exception {
+ public void should_return_null_for_final_class_if_unsupported() throws Throwable {
assumeFalse(Plugins.getMockMaker().isTypeMockable(Baz.class).mockable());
- assertNull(values.returnValueFor(Baz.class));
+ assertNull(values.answer(invocationOf(AllInterface.class, "getFinalClass")));
}
@Test
diff --git a/src/test/java/org/mockito/internal/util/reflection/GenericMetadataSupportTest.java b/src/test/java/org/mockito/internal/util/reflection/GenericMetadataSupportTest.java
index 323efbf843..cc5b5885d1 100644
--- a/src/test/java/org/mockito/internal/util/reflection/GenericMetadataSupportTest.java
+++ b/src/test/java/org/mockito/internal/util/reflection/GenericMetadataSupportTest.java
@@ -8,9 +8,16 @@
import java.io.Serializable;
import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
@@ -21,32 +28,67 @@ public class GenericMetadataSupportTest {
interface GenericsSelfReference> {
T self();
}
+
interface UpperBoundedTypeWithClass> {
E get();
}
+
interface UpperBoundedTypeWithInterfaces & Cloneable> {
E get();
}
- interface ListOfNumbers extends List {}
- interface AnotherListOfNumbers extends ListOfNumbers {}
- abstract class ListOfNumbersImpl implements ListOfNumbers {}
- abstract class AnotherListOfNumbersImpl extends ListOfNumbersImpl {}
+ interface ListOfNumbers extends List {
+ }
+
+ interface AnotherListOfNumbers extends ListOfNumbers {
+ }
+
+ abstract class ListOfNumbersImpl implements ListOfNumbers {
+ }
+
+ abstract class AnotherListOfNumbersImpl extends ListOfNumbersImpl {
+ }
- interface ListOfAnyNumbers extends List {}
+ interface ListOfAnyNumbers extends List {
+ }
interface GenericsNest & Cloneable> extends Map> {
Set remove(Object key); // override with fixed ParameterizedType
+
List super Integer> returning_wildcard_with_class_lower_bound();
+
List super K> returning_wildcard_with_typeVar_lower_bound();
+
List extends K> returning_wildcard_with_typeVar_upper_bound();
+
K returningK();
+
List paramType_with_type_params();
+
T two_type_params();
+
O typeVar_with_type_params();
}
- static class StringList extends ArrayList { }
+ static class StringList extends ArrayList {
+ }
+
+ public interface TopInterface {
+ T generic();
+ }
+
+ public interface MiddleInterface extends TopInterface {
+ }
+
+ public class OwningClassWithDeclaredUpperBounds & Comparable & Cloneable> {
+ public abstract class AbstractInner implements MiddleInterface {
+ }
+ }
+
+ public class OwningClassWithNoDeclaredUpperBounds {
+ public abstract class AbstractInner implements MiddleInterface {
+ }
+ }
@Test
public void typeVariable_of_self_type() {
@@ -56,7 +98,7 @@ public void typeVariable_of_self_type() {
}
@Test
- public void can_get_raw_type_from_Class() throws Exception {
+ public void can_get_raw_type_from_Class() {
assertThat(inferFrom(ListOfAnyNumbers.class).rawType()).isEqualTo(ListOfAnyNumbers.class);
assertThat(inferFrom(ListOfNumbers.class).rawType()).isEqualTo(ListOfNumbers.class);
assertThat(inferFrom(GenericsNest.class).rawType()).isEqualTo(GenericsNest.class);
@@ -64,7 +106,7 @@ public void can_get_raw_type_from_Class() throws Exception {
}
@Test
- public void can_get_raw_type_from_ParameterizedType() throws Exception {
+ public void can_get_raw_type_from_ParameterizedType() {
assertThat(inferFrom(ListOfAnyNumbers.class.getGenericInterfaces()[0]).rawType()).isEqualTo(List.class);
assertThat(inferFrom(ListOfNumbers.class.getGenericInterfaces()[0]).rawType()).isEqualTo(List.class);
assertThat(inferFrom(GenericsNest.class.getGenericInterfaces()[0]).rawType()).isEqualTo(Map.class);
@@ -72,7 +114,7 @@ public void can_get_raw_type_from_ParameterizedType() throws Exception {
}
@Test
- public void can_get_type_variables_from_Class() throws Exception {
+ public void can_get_type_variables_from_Class() {
assertThat(inferFrom(GenericsNest.class).actualTypeArguments().keySet()).hasSize(1).extracting("name").contains("K");
assertThat(inferFrom(ListOfNumbers.class).actualTypeArguments().keySet()).isEmpty();
assertThat(inferFrom(ListOfAnyNumbers.class).actualTypeArguments().keySet()).hasSize(1).extracting("name").contains("N");
@@ -89,7 +131,7 @@ public void can_resolve_type_variables_from_ancestors() throws Exception {
}
@Test
- public void can_get_type_variables_from_ParameterizedType() throws Exception {
+ public void can_get_type_variables_from_ParameterizedType() {
assertThat(inferFrom(GenericsNest.class.getGenericInterfaces()[0]).actualTypeArguments().keySet()).hasSize(2).extracting("name").contains("K", "V");
assertThat(inferFrom(ListOfAnyNumbers.class.getGenericInterfaces()[0]).actualTypeArguments().keySet()).hasSize(1).extracting("name").contains("E");
assertThat(inferFrom(Integer.class.getGenericInterfaces()[0]).actualTypeArguments().keySet()).hasSize(1).extracting("name").contains("T");
@@ -98,7 +140,7 @@ public void can_get_type_variables_from_ParameterizedType() throws Exception {
}
@Test
- public void typeVariable_return_type_of____iterator____resolved_to_Iterator_and_type_argument_to_String() throws Exception {
+ public void typeVariable_return_type_of____iterator____resolved_to_Iterator_and_type_argument_to_String() {
GenericMetadataSupport genericMetadata = inferFrom(StringList.class).resolveGenericReturnType(firstNamedMethod("iterator", StringList.class));
assertThat(genericMetadata.rawType()).isEqualTo(Iterator.class);
@@ -106,7 +148,7 @@ public void typeVariable_return_type_of____iterator____resolved_to_Iterator_and_
}
@Test
- public void typeVariable_return_type_of____get____resolved_to_Set_and_type_argument_to_Number() throws Exception {
+ public void typeVariable_return_type_of____get____resolved_to_Set_and_type_argument_to_Number() {
GenericMetadataSupport genericMetadata = inferFrom(GenericsNest.class).resolveGenericReturnType(firstNamedMethod("get", GenericsNest.class));
assertThat(genericMetadata.rawType()).isEqualTo(Set.class);
@@ -114,7 +156,7 @@ public void typeVariable_return_type_of____get____resolved_to_Set_and_type_argum
}
@Test
- public void bounded_typeVariable_return_type_of____returningK____resolved_to_Comparable_and_with_BoundedType() throws Exception {
+ public void bounded_typeVariable_return_type_of____returningK____resolved_to_Comparable_and_with_BoundedType() {
GenericMetadataSupport genericMetadata = inferFrom(GenericsNest.class).resolveGenericReturnType(firstNamedMethod("returningK", GenericsNest.class));
assertThat(genericMetadata.rawType()).isEqualTo(Comparable.class);
@@ -123,7 +165,7 @@ public void bounded_typeVariable_return_type_of____returningK____resolved_to_Com
}
@Test
- public void fixed_ParamType_return_type_of____remove____resolved_to_Set_and_type_argument_to_Number() throws Exception {
+ public void fixed_ParamType_return_type_of____remove____resolved_to_Set_and_type_argument_to_Number() {
GenericMetadataSupport genericMetadata = inferFrom(GenericsNest.class).resolveGenericReturnType(firstNamedMethod("remove", GenericsNest.class));
assertThat(genericMetadata.rawType()).isEqualTo(Set.class);
@@ -131,7 +173,7 @@ public void fixed_ParamType_return_type_of____remove____resolved_to_Set_and_type
}
@Test
- public void paramType_return_type_of____values____resolved_to_Collection_and_type_argument_to_Parameterized_Set() throws Exception {
+ public void paramType_return_type_of____values____resolved_to_Collection_and_type_argument_to_Parameterized_Set() {
GenericMetadataSupport genericMetadata = inferFrom(GenericsNest.class).resolveGenericReturnType(firstNamedMethod("values", GenericsNest.class));
assertThat(genericMetadata.rawType()).isEqualTo(Collection.class);
@@ -141,7 +183,7 @@ public void paramType_return_type_of____values____resolved_to_Collection_and_typ
}
@Test
- public void paramType_with_type_parameters_return_type_of____paramType_with_type_params____resolved_to_Collection_and_type_argument_to_Parameterized_Set() throws Exception {
+ public void paramType_with_type_parameters_return_type_of____paramType_with_type_params____resolved_to_Collection_and_type_argument_to_Parameterized_Set() {
GenericMetadataSupport genericMetadata = inferFrom(GenericsNest.class).resolveGenericReturnType(firstNamedMethod("paramType_with_type_params", GenericsNest.class));
assertThat(genericMetadata.rawType()).isEqualTo(List.class);
@@ -150,7 +192,7 @@ public void paramType_with_type_parameters_return_type_of____paramType_with_type
}
@Test
- public void typeVariable_with_type_parameters_return_type_of____typeVar_with_type_params____resolved_K_hence_to_Comparable_and_with_BoundedType() throws Exception {
+ public void typeVariable_with_type_parameters_return_type_of____typeVar_with_type_params____resolved_K_hence_to_Comparable_and_with_BoundedType() {
GenericMetadataSupport genericMetadata = inferFrom(GenericsNest.class).resolveGenericReturnType(firstNamedMethod("typeVar_with_type_params", GenericsNest.class));
assertThat(genericMetadata.rawType()).isEqualTo(Comparable.class);
@@ -159,7 +201,7 @@ public void typeVariable_with_type_parameters_return_type_of____typeVar_with_typ
}
@Test
- public void class_return_type_of____append____resolved_to_StringBuilder_and_type_arguments() throws Exception {
+ public void class_return_type_of____append____resolved_to_StringBuilder_and_type_arguments() {
GenericMetadataSupport genericMetadata = inferFrom(StringBuilder.class).resolveGenericReturnType(firstNamedMethod("append", StringBuilder.class));
assertThat(genericMetadata.rawType()).isEqualTo(StringBuilder.class);
@@ -167,9 +209,8 @@ public void class_return_type_of____append____resolved_to_StringBuilder_and_type
}
-
@Test
- public void paramType_with_wildcard_return_type_of____returning_wildcard_with_class_lower_bound____resolved_to_List_and_type_argument_to_Integer() throws Exception {
+ public void paramType_with_wildcard_return_type_of____returning_wildcard_with_class_lower_bound____resolved_to_List_and_type_argument_to_Integer() {
GenericMetadataSupport genericMetadata = inferFrom(GenericsNest.class).resolveGenericReturnType(firstNamedMethod("returning_wildcard_with_class_lower_bound", GenericsNest.class));
assertThat(genericMetadata.rawType()).isEqualTo(List.class);
@@ -179,17 +220,18 @@ public void paramType_with_wildcard_return_type_of____returning_wildcard_with_cl
}
@Test
- public void paramType_with_wildcard_return_type_of____returning_wildcard_with_typeVar_lower_bound____resolved_to_List_and_type_argument_to_Integer() throws Exception {
+ public void paramType_with_wildcard_return_type_of____returning_wildcard_with_typeVar_lower_bound____resolved_to_List_and_type_argument_to_Integer() {
GenericMetadataSupport genericMetadata = inferFrom(GenericsNest.class).resolveGenericReturnType(firstNamedMethod("returning_wildcard_with_typeVar_lower_bound", GenericsNest.class));
assertThat(genericMetadata.rawType()).isEqualTo(List.class);
GenericMetadataSupport.BoundedType boundedType = (GenericMetadataSupport.BoundedType) typeVariableValue(genericMetadata.actualTypeArguments(), "E");
assertThat(inferFrom(boundedType.firstBound()).rawType()).isEqualTo(Comparable.class);
- assertThat(boundedType.interfaceBounds()).contains(Cloneable.class); }
+ assertThat(boundedType.interfaceBounds()).contains(Cloneable.class);
+ }
@Test
- public void paramType_with_wildcard_return_type_of____returning_wildcard_with_typeVar_upper_bound____resolved_to_List_and_type_argument_to_Integer() throws Exception {
+ public void paramType_with_wildcard_return_type_of____returning_wildcard_with_typeVar_upper_bound____resolved_to_List_and_type_argument_to_Integer() {
GenericMetadataSupport genericMetadata = inferFrom(GenericsNest.class).resolveGenericReturnType(firstNamedMethod("returning_wildcard_with_typeVar_upper_bound", GenericsNest.class));
assertThat(genericMetadata.rawType()).isEqualTo(List.class);
@@ -199,6 +241,75 @@ public void paramType_with_wildcard_return_type_of____returning_wildcard_with_ty
assertThat(boundedType.interfaceBounds()).contains(Cloneable.class);
}
+ @Test
+ public void can_extract_raw_type_from_bounds_on_terminal_typeVariable() {
+ assertThat(inferFrom(OwningClassWithDeclaredUpperBounds.AbstractInner.class)
+ .resolveGenericReturnType(firstNamedMethod("generic", OwningClassWithDeclaredUpperBounds.AbstractInner.class))
+ .rawType()
+ ).isEqualTo(List.class);
+ assertThat(inferFrom(OwningClassWithNoDeclaredUpperBounds.AbstractInner.class)
+ .resolveGenericReturnType(firstNamedMethod("generic", OwningClassWithNoDeclaredUpperBounds.AbstractInner.class))
+ .rawType()
+ ).isEqualTo(Object.class);
+ }
+
+ @Test
+ public void can_extract_interface_type_from_bounds_on_terminal_typeVariable() {
+
+ assertThat(inferFrom(OwningClassWithDeclaredUpperBounds.AbstractInner.class)
+ .resolveGenericReturnType(firstNamedMethod("generic", OwningClassWithDeclaredUpperBounds.AbstractInner.class))
+ .rawExtraInterfaces()
+ ).containsExactly(Comparable.class, Cloneable.class);
+ assertThat(inferFrom(OwningClassWithDeclaredUpperBounds.AbstractInner.class)
+ .resolveGenericReturnType(firstNamedMethod("generic", OwningClassWithDeclaredUpperBounds.AbstractInner.class))
+ .extraInterfaces()
+ ).containsExactly(parameterizedTypeOf(Comparable.class, null, String.class),
+ Cloneable.class);
+
+ assertThat(inferFrom(OwningClassWithNoDeclaredUpperBounds.AbstractInner.class)
+ .resolveGenericReturnType(firstNamedMethod("generic", OwningClassWithNoDeclaredUpperBounds.AbstractInner.class))
+ .extraInterfaces()
+ ).isEmpty();
+ }
+
+ private ParameterizedType parameterizedTypeOf(final Class> rawType, final Class> ownerType, final Type... actualTypeArguments) {
+ return new ParameterizedType() {
+ @Override
+ public Type[] getActualTypeArguments() {
+ return actualTypeArguments;
+ }
+
+ @Override
+ public Type getRawType() {
+ return rawType;
+ }
+
+ @Override
+ public Type getOwnerType() {
+ return ownerType;
+ }
+
+ public boolean equals(Object other) {
+ if (other instanceof ParameterizedType) {
+ ParameterizedType otherParamType = (ParameterizedType) other;
+ if (this == otherParamType) {
+ return true;
+ } else {
+ return equals(ownerType, otherParamType.getOwnerType())
+ && equals(rawType, otherParamType.getRawType())
+ && Arrays.equals(actualTypeArguments, otherParamType.getActualTypeArguments());
+ }
+ } else {
+ return false;
+ }
+ }
+
+ private boolean equals(Object a, Object b) {
+ return (a == b) || (a != null && a.equals(b));
+ }
+ };
+ }
+
private Type typeVariableValue(Map, Type> typeVariables, String typeVariableName) {
for (Map.Entry, Type> typeVariableTypeEntry : typeVariables.entrySet()) {
if (typeVariableTypeEntry.getKey().getName().equals(typeVariableName)) {
diff --git a/src/test/java/org/mockitousage/debugging/StubbingLookupListenerCallbackTest.java b/src/test/java/org/mockitousage/debugging/StubbingLookupListenerCallbackTest.java
new file mode 100644
index 0000000000..280fc61962
--- /dev/null
+++ b/src/test/java/org/mockitousage/debugging/StubbingLookupListenerCallbackTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2018 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockitousage.debugging;
+
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.InOrder;
+import org.mockito.listeners.StubbingLookupEvent;
+import org.mockito.listeners.StubbingLookupListener;
+import org.mockito.mock.MockCreationSettings;
+import org.mockitousage.IMethods;
+import org.mockitoutil.ConcurrentTesting;
+import org.mockitoutil.TestBase;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.*;
+
+public class StubbingLookupListenerCallbackTest extends TestBase {
+
+ StubbingLookupListener listener = mock(StubbingLookupListener.class);
+ StubbingLookupListener listener2 = mock(StubbingLookupListener.class);
+ Foo mock = mock(Foo.class, withSettings().stubbingLookupListeners(listener));
+
+ @Test
+ public void should_call_listener_when_mock_return_normally_with_stubbed_answer() {
+ // given
+ doReturn("coke").when(mock).giveMeSomeString("soda");
+ doReturn("java").when(mock).giveMeSomeString("coffee");
+
+ // when
+ mock.giveMeSomeString("soda");
+
+ // then
+ verify(listener).onStubbingLookup(argThat(new ArgumentMatcher() {
+ @Override
+ public boolean matches(StubbingLookupEvent argument) {
+ assertEquals("soda", argument.getInvocation().getArgument(0));
+ assertEquals("mock", argument.getMockSettings().getMockName().toString());
+ assertEquals(2, argument.getAllStubbings().size());
+ assertNotNull(argument.getStubbingFound());
+ return true;
+ }
+ }));
+ }
+
+ @Test
+ public void should_call_listener_when_mock_return_normally_with_default_answer() {
+ // given
+ doReturn("java").when(mock).giveMeSomeString("coffee");
+
+ // when
+ mock.giveMeSomeString("soda");
+
+ // then
+ verify(listener).onStubbingLookup(argThat(new ArgumentMatcher() {
+ @Override
+ public boolean matches(StubbingLookupEvent argument) {
+ assertEquals("soda", argument.getInvocation().getArgument(0));
+ assertEquals("mock", argument.getMockSettings().getMockName().toString());
+ assertEquals(1, argument.getAllStubbings().size());
+ assertNull(argument.getStubbingFound());
+ return true;
+ }
+ }));
+ }
+
+ @Test
+ public void should_not_call_listener_when_mock_is_not_called() {
+ // when stubbing is recorded
+ doReturn("java").when(mock).giveMeSomeString("coffee");
+
+ // then
+ verifyZeroInteractions(listener);
+ }
+
+ @Test
+ public void should_allow_same_listener() {
+ // given
+ Foo mock = mock(Foo.class, withSettings().stubbingLookupListeners(listener, listener));
+
+ // when
+ mock.giveMeSomeString("tea");
+ mock.giveMeSomeString("coke");
+
+ // then each listener was notified 2 times (notified 4 times in total)
+ verify(listener, times(4)).onStubbingLookup(any(StubbingLookupEvent.class));
+ }
+
+ @Test
+ public void should_call_all_listeners_in_order() {
+ // given
+ Foo mock = mock(Foo.class, withSettings().stubbingLookupListeners(listener, listener2));
+ doReturn("sprite").when(mock).giveMeSomeString("soda");
+
+ // when
+ mock.giveMeSomeString("soda");
+
+ // then
+ InOrder inOrder = inOrder(listener, listener2);
+ inOrder.verify(listener).onStubbingLookup(any(StubbingLookupEvent.class));
+ inOrder.verify(listener2).onStubbingLookup(any(StubbingLookupEvent.class));
+ }
+
+ @Test
+ public void should_call_all_listeners_when_mock_throws_exception() {
+ // given
+ Foo mock = mock(Foo.class, withSettings().stubbingLookupListeners(listener, listener2));
+ doThrow(new NoWater()).when(mock).giveMeSomeString("tea");
+
+ // when
+ try {
+ mock.giveMeSomeString("tea");
+ fail();
+ } catch (NoWater e) {
+ // then
+ verify(listener).onStubbingLookup(any(StubbingLookupEvent.class));
+ verify(listener2).onStubbingLookup(any(StubbingLookupEvent.class));
+ }
+ }
+
+ @Test
+ public void should_delete_listener() {
+ // given
+ Foo mock = mock(Foo.class, withSettings().stubbingLookupListeners(listener, listener2));
+
+ // when
+ mock.doSomething("1");
+ mockingDetails(mock).getMockCreationSettings().getStubbingLookupListeners().remove(listener2);
+ mock.doSomething("2");
+
+ // then
+ verify(listener, times(2)).onStubbingLookup(any(StubbingLookupEvent.class));
+ verify(listener2, times(1)).onStubbingLookup(any(StubbingLookupEvent.class));
+ }
+
+ @Test
+ public void should_clear_listeners() {
+ // given
+ Foo mock = mock(Foo.class, withSettings().stubbingLookupListeners(listener, listener2));
+
+ // when
+ mockingDetails(mock).getMockCreationSettings().getStubbingLookupListeners().clear();
+ mock.doSomething("foo");
+
+ // then
+ verifyZeroInteractions(listener, listener2);
+ }
+
+ @Test
+ public void add_listeners_concurrently_sanity_check() throws Exception {
+ //given
+ final IMethods mock = mock(IMethods.class);
+ final MockCreationSettings> settings = mockingDetails(mock).getMockCreationSettings();
+
+ List runnables = new LinkedList();
+ for (int i = 0; i < 50; i++) {
+ runnables.add(new Runnable() {
+ public void run() {
+ StubbingLookupListener listener1 = mock(StubbingLookupListener.class);
+ StubbingLookupListener listener2 = mock(StubbingLookupListener.class);
+ settings.getStubbingLookupListeners().add(listener1);
+ settings.getStubbingLookupListeners().add(listener2);
+ settings.getStubbingLookupListeners().remove(listener1);
+ }
+ });
+ }
+
+ //when
+ ConcurrentTesting.concurrently(runnables.toArray(new Runnable[runnables.size()]));
+
+ //then
+ //This assertion may be flaky. If it is let's fix it or remove the test. For now, I'm keeping the test.
+ assertEquals(50, settings.getStubbingLookupListeners().size());
+ }
+
+ private static class NoWater extends RuntimeException {}
+}
diff --git a/src/test/java/org/mockitousage/junitrule/VerificationCollectorImplTest.java b/src/test/java/org/mockitousage/junitrule/VerificationCollectorImplTest.java
index 8f4c420eca..9b4cc1ec1c 100644
--- a/src/test/java/org/mockitousage/junitrule/VerificationCollectorImplTest.java
+++ b/src/test/java/org/mockitousage/junitrule/VerificationCollectorImplTest.java
@@ -8,13 +8,14 @@
import org.junit.Test;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
+import org.mockito.ArgumentMatcher;
import org.mockito.exceptions.base.MockitoAssertionError;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.VerificationCollector;
import org.mockitousage.IMethods;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
+import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import static org.mockito.Mockito.*;
public class VerificationCollectorImplTest {
@@ -46,14 +47,54 @@ public void should_collect_multiple_verification_failures() {
IMethods methods = mock(IMethods.class);
+ methods.intArgumentMethod(6);
+
verify(methods).simpleMethod();
verify(methods).byteReturningMethod();
+ verify(methods).intArgumentMethod(8);
+ verify(methods).longArg(8L);
try {
collector.collectAndReport();
- fail();
+ failBecauseExceptionWasNotThrown(MockitoAssertionError.class);
} catch (MockitoAssertionError error) {
assertThat(error).hasMessageContaining("1. Wanted but not invoked:");
assertThat(error).hasMessageContaining("2. Wanted but not invoked:");
+ assertThat(error).hasMessageContaining("3. Argument(s) are different! Wanted:");
+ assertThat(error).hasMessageContaining("4. Wanted but not invoked:");
+ }
+ }
+
+ @Test
+ public void should_collect_matching_error_from_non_matching_arguments() {
+ VerificationCollector collector = MockitoJUnit.collector().assertLazily();
+
+ IMethods methods = mock(IMethods.class);
+
+ methods.intArgumentMethod(6);
+ methods.longArg(8L);
+ methods.forShort((short)6);
+
+ verify(methods).intArgumentMethod(8);
+ verify(methods).longArg(longThat(new ArgumentMatcher() {
+ @Override
+ public boolean matches(Long argument) {
+ throw new AssertionError("custom error message");
+ }
+ }));
+ verify(methods).forShort(shortThat(new ArgumentMatcher() {
+ @Override
+ public boolean matches(Short argument) {
+ return false;
+ }
+ }));
+
+ try {
+ collector.collectAndReport();
+ failBecauseExceptionWasNotThrown(MockitoAssertionError.class);
+ } catch (MockitoAssertionError error) {
+ assertThat(error).hasMessageContaining("1. Argument(s) are different! Wanted:");
+ assertThat(error).hasMessageContaining("2. custom error message");
+ assertThat(error).hasMessageContaining("3. Argument(s) are different! Wanted:");
}
}
@@ -66,7 +107,7 @@ public void should_only_collect_failures_ignore_successful_verifications() {
verify(methods, never()).simpleMethod();
verify(methods).byteReturningMethod();
- this.assertAtLeastOneFailure(collector);
+ this.assertExactlyOneFailure(collector);
}
@Test
@@ -79,13 +120,13 @@ public void should_continue_collecting_after_failing_verification() {
verify(methods).byteReturningMethod();
verify(methods).simpleMethod();
- this.assertAtLeastOneFailure(collector);
+ this.assertExactlyOneFailure(collector);
}
- private void assertAtLeastOneFailure(VerificationCollector collector) {
+ private void assertExactlyOneFailure(VerificationCollector collector) {
try {
collector.collectAndReport();
- fail();
+ failBecauseExceptionWasNotThrown(MockitoAssertionError.class);
} catch (MockitoAssertionError error) {
assertThat(error).hasMessageContaining("1. Wanted but not invoked:");
assertThat(error.getMessage()).doesNotContain("2.");
@@ -97,8 +138,11 @@ public void should_invoke_collector_rule_after_test() {
JUnitCore runner = new JUnitCore();
Result result = runner.run(VerificationCollectorRuleInner.class);
- assertThat(result.getFailureCount()).isEqualTo(1);
- assertThat(result.getFailures().get(0).getMessage()).contains("1. Wanted but not invoked:");
+ assertThat(result.getFailureCount()).as("failureCount").isEqualTo(2);
+ assertThat(result.getFailures().get(0).getMessage()).as("failure1").contains("1. Wanted but not invoked:");
+ assertThat(result.getFailures().get(1).getMessage()).as("failure2")
+ .contains("1. Argument(s) are different! Wanted:")
+ .contains("2. Wanted but not invoked:");
}
// This class is picked up when running a test suite using an IDE. It fails on purpose.
@@ -121,5 +165,14 @@ public void should_not_fail() {
verify(methods).simpleMethod();
}
+
+ @Test
+ public void should_fail_with_args() {
+ IMethods methods = mock(IMethods.class);
+ methods.intArgumentMethod(8);
+
+ verify(methods).intArgumentMethod(9);
+ verify(methods).byteReturningMethod();
+ }
}
}
diff --git a/src/test/java/org/mockitousage/serialization/AcrossClassLoaderSerializationTest.java b/src/test/java/org/mockitousage/serialization/AcrossClassLoaderSerializationTest.java
index 240cf7d027..f8a262d15d 100644
--- a/src/test/java/org/mockitousage/serialization/AcrossClassLoaderSerializationTest.java
+++ b/src/test/java/org/mockitousage/serialization/AcrossClassLoaderSerializationTest.java
@@ -33,7 +33,7 @@ public void check_that_mock_can_be_serialized_in_a_classloader_and_deserialized_
byte[] bytes = create_mock_and_serialize_it_in_class_loader_A();
Object the_deserialized_mock = read_stream_and_deserialize_it_in_class_loader_B(bytes);
- assertThat(the_deserialized_mock.getClass().getName()).startsWith("org.mockito.codegenAClassToBeMockedInThisTestOnlyAndInCallablesOnly");
+ assertThat(the_deserialized_mock.getClass().getName()).startsWith("org.mockito.codegen.AClassToBeMockedInThisTestOnlyAndInCallablesOnly");
}
private Object read_stream_and_deserialize_it_in_class_loader_B(byte[] bytes) throws Exception {
diff --git a/src/test/java/org/mockitousage/serialization/DeepStubsSerializableTest.java b/src/test/java/org/mockitousage/serialization/DeepStubsSerializableTest.java
index 3294614c24..c26e8d8b57 100644
--- a/src/test/java/org/mockitousage/serialization/DeepStubsSerializableTest.java
+++ b/src/test/java/org/mockitousage/serialization/DeepStubsSerializableTest.java
@@ -11,7 +11,11 @@
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.*;
+import static org.assertj.core.api.Assertions.fail;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
import static org.mockitoutil.SimpleSerializationUtil.serializeAndBack;
public class DeepStubsSerializableTest {
@@ -45,7 +49,7 @@ public void should_serialize_and_deserialize_parameterized_class_mocked_with_dee
assertThat(deserialized_deep_stub.iterator().next().add("yes")).isEqualTo(true);
}
- @Test(expected = ClassCastException.class)
+ @Test
public void should_discard_generics_metadata_when_serialized_then_disabling_deep_stubs_with_generics() throws Exception {
// given
ListContainer deep_stubbed = mock(ListContainer.class, withSettings().defaultAnswer(RETURNS_DEEP_STUBS).serializable());
@@ -53,10 +57,14 @@ public void should_discard_generics_metadata_when_serialized_then_disabling_deep
ListContainer deserialized_deep_stub = serializeAndBack(deep_stubbed);
- // when stubbing on a deserialized mock
- when(deserialized_deep_stub.iterator().next().get(42)).thenReturn("no");
-
- // then revert to the default RETURNS_DEEP_STUBS and the code will raise a ClassCastException
+ try {
+ // when stubbing on a deserialized mock
+ // then revert to the default RETURNS_DEEP_STUBS and the code will raise a ClassCastException
+ when(deserialized_deep_stub.iterator().next().get(42)).thenReturn("no");
+ fail("Expected an exception to be thrown as deep stubs and serialization does not play well together");
+ } catch (NullPointerException e) {
+ assertThat(e).hasMessage(null);
+ }
}
static class SampleClass implements Serializable {
diff --git a/src/test/java/org/mockitousage/serialization/StrictStubsSerializableTest.java b/src/test/java/org/mockitousage/serialization/StrictStubsSerializableTest.java
new file mode 100644
index 0000000000..9d786695fc
--- /dev/null
+++ b/src/test/java/org/mockitousage/serialization/StrictStubsSerializableTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockitousage.serialization;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.Serializable;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.*;
+import static org.mockitoutil.SimpleSerializationUtil.serializeAndBack;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class StrictStubsSerializableTest {
+
+ @Mock(serializable = true) private SampleClass sampleClass;
+
+ @Test
+ public void should_serialize_and_deserialize_mock_created_with_serializable_and_strict_stubs() throws Exception {
+ // given
+ when(sampleClass.isFalse()).thenReturn(true);
+
+ // when
+ SampleClass deserializedSample = serializeAndBack(sampleClass);
+ // to satisfy strict stubbing
+ deserializedSample.isFalse();
+ verify(deserializedSample).isFalse();
+ verify(sampleClass, never()).isFalse();
+
+ // then
+ assertThat(deserializedSample.isFalse()).isEqualTo(true);
+ assertThat(sampleClass.isFalse()).isEqualTo(true);
+ }
+
+ static class SampleClass implements Serializable {
+
+ boolean isFalse() {
+ return false;
+ }
+ }
+}
diff --git a/src/test/java/org/mockitousage/stubbing/DeepStubbingTest.java b/src/test/java/org/mockitousage/stubbing/DeepStubbingTest.java
index df41f062fb..0e574f0c5e 100644
--- a/src/test/java/org/mockitousage/stubbing/DeepStubbingTest.java
+++ b/src/test/java/org/mockitousage/stubbing/DeepStubbingTest.java
@@ -224,7 +224,7 @@ public void withPatternPrimitive() throws Exception {
Person person = mock(Person.class, RETURNS_DEEP_STUBS);
@Test
- public void shouldStubbingBasicallyWorkFine() throws Exception {
+ public void shouldStubbingBasicallyWorkFine() {
//given
given(person.getAddress().getStreet().getName()).willReturn("Norymberska");
@@ -236,7 +236,7 @@ public void shouldStubbingBasicallyWorkFine() throws Exception {
}
@Test
- public void shouldVerificationBasicallyWorkFine() throws Exception {
+ public void shouldVerificationBasicallyWorkFine() {
//given
person.getAddress().getStreet().getName();
@@ -245,7 +245,7 @@ public void shouldVerificationBasicallyWorkFine() throws Exception {
}
@Test
- public void verification_work_with_argument_Matchers_in_nested_calls() throws Exception {
+ public void verification_work_with_argument_Matchers_in_nested_calls() {
//given
person.getAddress("111 Mock Lane").getStreet();
person.getAddress("111 Mock Lane").getStreet(Locale.ITALIAN).getName();
@@ -257,7 +257,7 @@ public void verification_work_with_argument_Matchers_in_nested_calls() throws Ex
}
@Test
- public void deep_stub_return_same_mock_instance_if_invocation_matchers_matches() throws Exception {
+ public void deep_stub_return_same_mock_instance_if_invocation_matchers_matches() {
when(person.getAddress(anyString()).getStreet().getName()).thenReturn("deep");
person.getAddress("the docks").getStreet().getName();
@@ -270,7 +270,7 @@ public void deep_stub_return_same_mock_instance_if_invocation_matchers_matches()
}
@Test
- public void times_never_atLeast_atMost_verificationModes_should_work() throws Exception {
+ public void times_never_atLeast_atMost_verificationModes_should_work() {
when(person.getAddress(anyString()).getStreet().getName()).thenReturn("deep");
person.getAddress("the docks").getStreet().getName();
@@ -285,7 +285,7 @@ public void times_never_atLeast_atMost_verificationModes_should_work() throws Ex
@Test
- public void inOrder_only_work_on_the_very_last_mock_but_it_works() throws Exception {
+ public void inOrder_only_work_on_the_very_last_mock_but_it_works() {
when(person.getAddress(anyString()).getStreet().getName()).thenReturn("deep");
when(person.getAddress(anyString()).getStreet(Locale.ITALIAN).getName()).thenReturn("deep");
when(person.getAddress(anyString()).getStreet(Locale.CHINESE).getName()).thenReturn("deep");
@@ -307,7 +307,7 @@ public void inOrder_only_work_on_the_very_last_mock_but_it_works() throws Except
}
@Test
- public void verificationMode_only_work_on_the_last_returned_mock() throws Exception {
+ public void verificationMode_only_work_on_the_last_returned_mock() {
// 1st invocation on Address mock (stubbing)
when(person.getAddress("the docks").getStreet().getName()).thenReturn("deep");
@@ -328,7 +328,7 @@ public void verificationMode_only_work_on_the_last_returned_mock() throws Except
}
@Test
- public void shouldFailGracefullyWhenClassIsFinal() throws Exception {
+ public void shouldFailGracefullyWhenClassIsFinal() {
//when
FinalClass value = new FinalClass();
given(person.getFinalClass()).willReturn(value);
diff --git a/src/test/java/org/mockitousage/stubbing/SmartNullsStubbingTest.java b/src/test/java/org/mockitousage/stubbing/SmartNullsStubbingTest.java
index 9b3dc868d1..8e27f73100 100644
--- a/src/test/java/org/mockitousage/stubbing/SmartNullsStubbingTest.java
+++ b/src/test/java/org/mockitousage/stubbing/SmartNullsStubbingTest.java
@@ -42,6 +42,15 @@ public void shouldSmartNPEPointToUnstubbedCall() throws Exception {
}
}
+ @Test
+ public void should_not_throw_NPE_when_verifying_with_returns_smart_nulls() {
+ Foo mock = mock(Foo.class, RETURNS_SMART_NULLS);
+
+ when(mock.returnsFromArg(null)).thenReturn("Does not fail.");
+
+ assertThat((Object) mock.returnsFromArg(null)).isEqualTo("Does not fail.");
+ }
+
interface Bar {
void boo();
}
@@ -59,6 +68,8 @@ Bar getBarWithParams(int x, String y) {
return null;
}
+ T returnsFromArg(T arg) { return arg; }
+
void boo() {}
}
diff --git a/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleHandlingTest.java b/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleHandlingTest.java
index 6ec32893a6..ed732c8089 100644
--- a/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleHandlingTest.java
+++ b/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleHandlingTest.java
@@ -4,6 +4,7 @@
*/
package org.mockito.moduletest;
+import java.util.concurrent.locks.Lock;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.loading.ClassInjector;
@@ -91,6 +92,34 @@ public void can_define_class_in_open_reading_module() throws Exception {
}
}
+ @Test
+ public void can_define_class_in_open_java_util_module() throws Exception {
+ assumeThat(Plugins.getMockMaker() instanceof InlineByteBuddyMockMaker, is(false));
+
+ Path jar = modularJar(true, true, true);
+ ModuleLayer layer = layer(jar, true);
+
+ ClassLoader loader = layer.findLoader("mockito.test");
+ Class> type = loader.loadClass("java.util.concurrent.locks.Lock");
+
+ ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(loader);
+ try {
+ Class> mockito = loader.loadClass(Mockito.class.getName());
+ @SuppressWarnings("unchecked")
+ Lock mock = (Lock) mockito.getMethod("mock", Class.class).invoke(null, type);
+ Object stubbing = mockito.getMethod("when", Object.class).invoke(null, mock.tryLock());
+ loader.loadClass(OngoingStubbing.class.getName()).getMethod("thenReturn", Object.class).invoke(stubbing, true);
+
+ boolean relocated = !Boolean.getBoolean("org.mockito.internal.noUnsafeInjection") && ClassInjector.UsingReflection.isAvailable();
+ String prefix = relocated ? "org.mockito.codegen.Lock$MockitoMock$" : "java.util.concurrent.locks.Lock$MockitoMock$";
+ assertThat(mock.getClass().getName()).startsWith(prefix);
+ assertThat(mock.tryLock()).isEqualTo(true);
+ } finally {
+ Thread.currentThread().setContextClassLoader(contextLoader);
+ }
+ }
+
@Test
public void inline_mock_maker_can_mock_closed_modules() throws Exception {
assumeThat(Plugins.getMockMaker() instanceof InlineByteBuddyMockMaker, is(true));
diff --git a/version.properties b/version.properties
index 21052c6954..0a877c4d56 100644
--- a/version.properties
+++ b/version.properties
@@ -2,7 +2,7 @@
version=2.25.0
#Previous version used to generate release notes delta
-previousVersion=2.24.1
+previousVersion=2.24.10
#Not published currently, see https://github.com/mockito/mockito/issues/962
mockito.testng.version=1.0