returnsArgAt(int position) {
* This feature suffers from the same drawback as the spy.
* The mock will call the delegate if you use regular when().then() stubbing style.
* Since the real implementation is called this might have some side effects.
- * Therefore you should use the doReturn|Throw|Answer|CallRealMethod stubbing style. Example:
+ * Therefore, you should use the doReturn|Throw|Answer|CallRealMethod stubbing style. Example:
*
*
* List listWithDelegate = mock(List.class, AdditionalAnswers.delegatesTo(awesomeList));
diff --git a/src/main/java/org/mockito/ArgumentCaptor.java b/src/main/java/org/mockito/ArgumentCaptor.java
index afb3add2ba..e9b347bd2e 100644
--- a/src/main/java/org/mockito/ArgumentCaptor.java
+++ b/src/main/java/org/mockito/ArgumentCaptor.java
@@ -35,12 +35,13 @@
*
*
* Warning: it is recommended to use ArgumentCaptor with verification but not with stubbing.
- * Using ArgumentCaptor with stubbing may decrease test readability because captor is created outside of assert (aka verify or 'then') block.
- * Also it may reduce defect localization because if stubbed method was not called then no argument is captured.
+ * Using ArgumentCaptor with stubbing may decrease test readability because captor is created
+ * outside of assertion (aka verify or 'then') blocks.
+ * It may also reduce defect localization because if the stubbed method was not called, then no argument is captured.
*
*
* In a way ArgumentCaptor is related to custom argument matchers (see javadoc for {@link ArgumentMatcher} class).
- * Both techniques can be used for making sure certain arguments were passed to mocks.
+ * Both techniques can be used for making sure certain arguments were passed to mock objects.
* However, ArgumentCaptor may be a better fit if:
*
* custom argument matcher is not likely to be reused
@@ -62,11 +63,12 @@
@CheckReturnValue
public class ArgumentCaptor {
- private final CapturingMatcher capturingMatcher = new CapturingMatcher();
+ private final CapturingMatcher capturingMatcher;
private final Class extends T> clazz;
private ArgumentCaptor(Class extends T> clazz) {
this.clazz = clazz;
+ this.capturingMatcher = new CapturingMatcher(clazz);
}
/**
diff --git a/src/main/java/org/mockito/ArgumentMatcher.java b/src/main/java/org/mockito/ArgumentMatcher.java
index d0324b6f3f..3c8ccdea00 100644
--- a/src/main/java/org/mockito/ArgumentMatcher.java
+++ b/src/main/java/org/mockito/ArgumentMatcher.java
@@ -10,7 +10,7 @@
* and reduce the risk of version incompatibility.
* Migration guide is included close to the bottom of this javadoc.
*
- * For non-trivial method arguments used in stubbing or verification, you have following options
+ * For non-trivial method arguments used in stubbing or verification, you have the following options
* (in no particular order):
*
* refactor the code so that the interactions with collaborators are easier to test with mocks.
@@ -102,14 +102,15 @@
*
*
*
- * What option is right for you? If you don't mind compile dependency to hamcrest
- * then option b) is probably right for you.
- * Your choice should not have big impact and is fully reversible -
- * you can choose different option in future (and refactor the code)
+ * What option is right for you? If you don't mind having a compile-time dependency for Hamcrest,
+ * then the second option is probably right for you.
+ * Your choice should not have a big impact and is fully reversible -
+ * you can choose different option in future (and refactor the code)!
*
* @param type of argument
* @since 2.1.0
*/
+@FunctionalInterface
public interface ArgumentMatcher {
/**
@@ -125,4 +126,44 @@ public interface ArgumentMatcher {
* @return true if this matcher accepts the given argument.
*/
boolean matches(T argument);
+
+ /**
+ * The type of the argument this matcher matches.
+ *
+ * This method is used to differentiate between a matcher used to match a raw vararg array parameter
+ * from a matcher used to match a single value passed as a vararg parameter.
+ *
+ *
Where the matcher:
+ *
+ * is at the parameter index of a vararg parameter
+ * is the last matcher passed
+ * this method returns a type assignable to the vararg parameter's raw type, i.e. its array type.
+ *
+ *
+ * ...then the matcher is matched against the raw vararg parameter, rather than the first element of the raw parameter.
+ *
+ * For example:
+ *
+ *
+ * // Given vararg method with signature:
+ * int someVarargMethod(String... args);
+ *
+ * // The following will match invocations with any number of parameters, i.e. any number of elements in the raw array.
+ * mock.someVarargMethod(isA(String[].class));
+ *
+ * // The following will match invocations with a single parameter, i.e. one string in the raw array.
+ * mock.someVarargMethod(isA(String.class));
+ *
+ * // The following will match invocations with two parameters, i.e. two strings in the raw array
+ * mock.someVarargMethod(isA(String.class), isA(String.class));
+ *
+ *
+ * Only matcher implementations that can conceptually match a raw vararg parameter should override this method.
+ *
+ * @return the type this matcher handles. The default value of {@link Void} means the type is not known.
+ * @since 4.11.0
+ */
+ default Class> type() {
+ return Void.class;
+ }
}
diff --git a/src/main/java/org/mockito/ArgumentMatchers.java b/src/main/java/org/mockito/ArgumentMatchers.java
index d969bc79e7..1743ce164b 100644
--- a/src/main/java/org/mockito/ArgumentMatchers.java
+++ b/src/main/java/org/mockito/ArgumentMatchers.java
@@ -14,6 +14,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.regex.Pattern;
import org.mockito.internal.matchers.Any;
@@ -174,7 +175,7 @@ public static T any() {
* @see #isNull()
*/
public static T any(Class type) {
- reportMatcher(new InstanceOf.VarArgAware(type, ""));
+ reportMatcher(new InstanceOf(type, ""));
return defaultValue(type);
}
@@ -699,6 +700,23 @@ public static T isNull() {
return null;
}
+ /**
+ * null
argument.
+ *
+ *
+ * See examples in javadoc for {@link ArgumentMatchers} class
+ *
+ *
+ * @param type the type of the argument being matched.
+ * @return null
.
+ * @see #isNotNull(Class)
+ * @since 4.11.0
+ */
+ public static T isNull(Class type) {
+ reportMatcher(new Null<>(type));
+ return null;
+ }
+
/**
* Not null
argument.
*
@@ -717,6 +735,26 @@ public static T notNull() {
return null;
}
+ /**
+ * Not null
argument.
+ *
+ *
+ * Alias to {@link ArgumentMatchers#isNotNull()}
+ *
+ *
+ *
+ * See examples in javadoc for {@link ArgumentMatchers} class
+ *
+ *
+ * @param type the type of the argument being matched.
+ * @return null
.
+ * @since 4.11.0
+ */
+ public static T notNull(Class type) {
+ reportMatcher(new NotNull<>(type));
+ return null;
+ }
+
/**
* Not null
argument.
*
@@ -735,6 +773,26 @@ public static T isNotNull() {
return notNull();
}
+ /**
+ * Not null
argument.
+ *
+ *
+ * Alias to {@link ArgumentMatchers#notNull(Class)}
+ *
+ *
+ *
+ * See examples in javadoc for {@link ArgumentMatchers} class
+ *
+ *
+ * @param type the type of the argument being matched.
+ * @return null
.
+ * @see #isNull()
+ * @since 4.11.0
+ */
+ public static T isNotNull(Class type) {
+ return notNull(type);
+ }
+
/**
* Argument that is either null
or of the given type.
*
@@ -856,8 +914,24 @@ public static T argThat(ArgumentMatcher matcher) {
}
/**
- * Allows creating custom char
argument matchers.
+ * Allows creating custom argument matchers where matching is considered successful when the consumer given by parameter does not throw an exception.
+ *
+ * Typically used with {@link Mockito#verify(Object)} to execute assertions on parameters passed to the verified method invocation.
*
+ * @param consumer executes assertions on the verified argument
+ * @return null
.
+ */
+ public static T assertArg(Consumer consumer) {
+ return argThat(
+ argument -> {
+ consumer.accept(argument);
+ return true;
+ });
+ }
+
+ /**
+ * Allows creating custom char
argument matchers.
+ *
* Note that {@link #argThat} will not work with primitive char
matchers due to NullPointerException
auto-unboxing caveat.
*
* See examples in javadoc for {@link ArgumentMatchers} class
@@ -872,7 +946,7 @@ public static char charThat(ArgumentMatcher matcher) {
/**
* Allows creating custom boolean
argument matchers.
- *
+ *
* Note that {@link #argThat} will not work with primitive boolean
matchers due to NullPointerException
auto-unboxing caveat.
*
* See examples in javadoc for {@link ArgumentMatchers} class
@@ -887,7 +961,7 @@ public static boolean booleanThat(ArgumentMatcher matcher) {
/**
* Allows creating custom byte
argument matchers.
- *
+ *
* Note that {@link #argThat} will not work with primitive byte
matchers due to NullPointerException
auto-unboxing caveat.
*
* See examples in javadoc for {@link ArgumentMatchers} class
@@ -902,7 +976,7 @@ public static byte byteThat(ArgumentMatcher matcher) {
/**
* Allows creating custom short
argument matchers.
- *
+ *
* Note that {@link #argThat} will not work with primitive short
matchers due to NullPointerException
auto-unboxing caveat.
*
* See examples in javadoc for {@link ArgumentMatchers} class
@@ -917,7 +991,7 @@ public static short shortThat(ArgumentMatcher matcher) {
/**
* Allows creating custom int
argument matchers.
- *
+ *
* Note that {@link #argThat} will not work with primitive int
matchers due to NullPointerException
auto-unboxing caveat.
*
* See examples in javadoc for {@link ArgumentMatchers} class
@@ -932,7 +1006,7 @@ public static int intThat(ArgumentMatcher matcher) {
/**
* Allows creating custom long
argument matchers.
- *
+ *
* Note that {@link #argThat} will not work with primitive long
matchers due to NullPointerException
auto-unboxing caveat.
*
* See examples in javadoc for {@link ArgumentMatchers} class
@@ -947,7 +1021,7 @@ public static long longThat(ArgumentMatcher matcher) {
/**
* Allows creating custom float
argument matchers.
- *
+ *
* Note that {@link #argThat} will not work with primitive float
matchers due to NullPointerException
auto-unboxing caveat.
*
* See examples in javadoc for {@link ArgumentMatchers} class
@@ -962,7 +1036,7 @@ public static float floatThat(ArgumentMatcher matcher) {
/**
* Allows creating custom double
argument matchers.
- *
+ *
* Note that {@link #argThat} will not work with primitive double
matchers due to NullPointerException
auto-unboxing caveat.
*
* See examples in javadoc for {@link ArgumentMatchers} class
diff --git a/src/main/java/org/mockito/InOrder.java b/src/main/java/org/mockito/InOrder.java
index 3cb047a4c4..ee71ae197c 100644
--- a/src/main/java/org/mockito/InOrder.java
+++ b/src/main/java/org/mockito/InOrder.java
@@ -12,13 +12,41 @@
* Allows verification in order. E.g:
*
*
+ * // Given
+ * First firstMock = mock(First.class);
+ * Second secondMock = mock(Second.class);
* InOrder inOrder = inOrder(firstMock, secondMock);
*
+ * // When
+ * firstMock.add("was called first");
+ * secondMock.add("was called second");
+ *
+ * // Then
* inOrder.verify(firstMock).add("was called first");
* inOrder.verify(secondMock).add("was called second");
+ * inOrder.verifyNoMoreInteractions();
+ *
+ *
+ * Static mocks can be verified alongside non-static mocks. E.g:
+ *
+ *
+ * // Given
+ * First firstMock = mock(First.class);
+ * MockedStatic staticSecondMock = mockStatic(StaticSecond.class);
+ * InOrder inOrder = inOrder(firstMock, StaticSecond.class);
+ *
+ * // When
+ * firstMock.add("was called first");
+ * StaticSecond.doSomething("foobar");
+ *
+ * // Then
+ * inOrder.verify(firstMock).add("was called first");
+ * inOrder.verify(staticSecondMock, () -> StaticSecond.doSomething("foobar"));
+ * inOrder.verifyNoMoreInteractions();
*
*
- * As of Mockito 1.8.4 you can verifyNoMoreInteractions() in order-sensitive way. Read more: {@link InOrder#verifyNoMoreInteractions()}
+ * As of Mockito 1.8.4 you can verifyNoMoreInteractions() in order-sensitive way. Read more:
+ * {@link InOrder#verifyNoMoreInteractions()}.
*
*
* See examples in javadoc for {@link Mockito} class
diff --git a/src/main/java/org/mockito/Incubating.java b/src/main/java/org/mockito/Incubating.java
index adf011abc7..ba209b3c41 100644
--- a/src/main/java/org/mockito/Incubating.java
+++ b/src/main/java/org/mockito/Incubating.java
@@ -5,6 +5,7 @@
package org.mockito;
import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -22,7 +23,13 @@
* and can change before release.
*
*
+ *
+ *
+ * Any components extending a class or interface annotated with this annotation should also
+ * be considered to be incubating, as the underlying implementation may be subject to future change
+ * before it is finalized.
*/
@Retention(RetentionPolicy.RUNTIME)
+@Inherited
@Documented
public @interface Incubating {}
diff --git a/src/main/java/org/mockito/InjectMocks.java b/src/main/java/org/mockito/InjectMocks.java
index ea8f188985..ff412b8df6 100644
--- a/src/main/java/org/mockito/InjectMocks.java
+++ b/src/main/java/org/mockito/InjectMocks.java
@@ -158,6 +158,11 @@
* be it mocks/spies or real objects.
*
*
+ *
+ * Elements annotated with this annotation can also be spied upon by also adding the {@link Spy}
+ * annotation to the element.
+ *
+ *
* @see Mock
* @see Spy
* @see MockitoAnnotations#openMocks(Object)
diff --git a/src/main/java/org/mockito/Mock.java b/src/main/java/org/mockito/Mock.java
index e8469b830d..4d94edfe26 100644
--- a/src/main/java/org/mockito/Mock.java
+++ b/src/main/java/org/mockito/Mock.java
@@ -13,6 +13,7 @@
import java.lang.annotation.Target;
import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.plugins.MockMaker;
import org.mockito.stubbing.Answer;
/**
@@ -124,6 +125,20 @@
*/
Strictness strictness() default Strictness.TEST_LEVEL_DEFAULT;
+ /**
+ * Mock will be created by the given {@link MockMaker}, see {@link MockSettings#mockMaker(String)}.
+ *
+ * @since 4.8.0
+ */
+ String mockMaker() default "";
+
+ /**
+ * Mock will not attempt to preserve all annotation metadata, see {@link MockSettings#withoutAnnotations()}.
+ *
+ * @since 5.3.0
+ */
+ boolean withoutAnnotations() default false;
+
enum Strictness {
/**
diff --git a/src/main/java/org/mockito/MockMakers.java b/src/main/java/org/mockito/MockMakers.java
new file mode 100644
index 0000000000..2f5459d222
--- /dev/null
+++ b/src/main/java/org/mockito/MockMakers.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2022 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito;
+
+import org.mockito.plugins.MockMaker;
+
+/**
+ * Constants for built-in implementations of {@code MockMaker}.
+ * You may use the constants of this class for {@link MockSettings#mockMaker(String)} or {@link Mock#mockMaker()}.
+ * The string values of these constants may also be used in the resource file mockito-extensions/org.mockito.plugins.MockMaker
+ * as described in the class documentation of {@link MockMaker}.
+ *
+ * @since 4.8.0
+ */
+public final class MockMakers {
+ /**
+ * Inline mock maker which can mock final types, enums and final methods.
+ * This mock maker cannot mock native methods,
+ * and it does not support {@link MockSettings#extraInterfaces(Class[]) extra interfaces}.
+ *
+ * @see Mocking final types, enums and final methods
+ */
+ public static final String INLINE = "mock-maker-inline";
+ /**
+ * Proxy mock maker which avoids code generation, but can only mock interfaces.
+ *
+ * @see Avoiding code generation when restricting mocks to interfaces
+ */
+ public static final String PROXY = "mock-maker-proxy";
+ /**
+ * Subclass mock maker which mocks types by creating subclasses.
+ * This is the first built-in mock maker which has been provided by Mockito.
+ * Since this mock maker relies on subclasses, it cannot mock final classes and methods.
+ */
+ public static final String SUBCLASS = "mock-maker-subclass";
+
+ private MockMakers() {}
+}
diff --git a/src/main/java/org/mockito/MockSettings.java b/src/main/java/org/mockito/MockSettings.java
index f838a8d121..e9c75c3a1e 100644
--- a/src/main/java/org/mockito/MockSettings.java
+++ b/src/main/java/org/mockito/MockSettings.java
@@ -5,6 +5,7 @@
package org.mockito;
import java.io.Serializable;
+import java.lang.reflect.Type;
import org.mockito.exceptions.misusing.PotentialStubbingProblem;
import org.mockito.exceptions.misusing.UnnecessaryStubbingException;
@@ -15,6 +16,7 @@
import org.mockito.listeners.VerificationStartedListener;
import org.mockito.mock.MockCreationSettings;
import org.mockito.mock.SerializableMode;
+import org.mockito.plugins.MockMaker;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
@@ -42,7 +44,7 @@
*
* {@link MockSettings} has been introduced for two reasons.
* Firstly, to make it easy to add another mock setting when the demand comes.
- * Secondly, to enable combining together different mock settings without introducing zillions of overloaded mock() methods.
+ * Secondly, to enable combining different mock settings without introducing zillions of overloaded mock() methods.
*/
@NotExtensible
public interface MockSettings extends Serializable {
@@ -63,7 +65,7 @@ public interface MockSettings extends Serializable {
* Baz baz = (Baz) foo;
*
*
- * @param interfaces extra interfaces the should implement.
+ * @param interfaces extra interfaces the mock should implement.
* @return settings instance so that you can fluently specify other settings
*/
MockSettings extraInterfaces(Class>... interfaces);
@@ -93,7 +95,7 @@ public interface MockSettings extends Serializable {
*
* Sets the instance that will be spied. Actually copies the internal fields of the passed instance to the mock.
*
- * As usual you are going to read the partial mock warning :
+ * As usual, you are going to read the partial mock warning :
* Object oriented programming is more or less about tackling complexity by dividing the complexity into separate, specific, SRPy objects.
* How does partial mock fit into this paradigm? Well, it just doesn't...
* Partial mock usually means that the complexity has been moved to a different method on the same object.
@@ -132,10 +134,10 @@ public interface MockSettings extends Serializable {
/**
* Specifies default answers to interactions.
- * It's quite advanced feature and typically you don't need it to write decent tests.
- * However it can be helpful when working with legacy systems.
+ * It's quite advanced feature, and typically you don't need it to write decent tests.
+ * However, it can be helpful when working with legacy systems.
*
- * It is the default answer so it will be used only when you don't stub the method call.
+ * It is the default answer, so it will be used only when you don't stub the method call.
*
*
* Foo mock = mock(Foo.class, withSettings().defaultAnswer(RETURNS_SMART_NULLS));
@@ -208,7 +210,7 @@ public interface MockSettings extends Serializable {
/**
* Add stubbing lookup listener to the mock object.
*
- * Multiple listeners may be added and they will be notified orderly.
+ * Multiple listeners may be added, and they will be notified in an orderly fashion.
*
* For use cases and more info see {@link StubbingLookupListener}.
*
@@ -227,7 +229,7 @@ public interface MockSettings extends Serializable {
* Registers a listener for method invocations on this mock. The listener is
* notified every time a method on this mock is called.
*
- * Multiple listeners may be added and they will be notified in the order they were supplied.
+ * Multiple listeners may be added, and they will be notified in the order they were supplied.
*
* Example:
*
@@ -246,7 +248,7 @@ public interface MockSettings extends Serializable {
* See {@link VerificationStartedListener} on how such listener can be useful.
*
* When multiple listeners are added, they are notified in order they were supplied.
- * There is no reason to supply multiple listeners but we wanted to keep the API
+ * There is no reason to supply multiple listeners, but we wanted to keep the API
* simple and consistent with {@link #invocationListeners(InvocationListener...)}.
*
* Throws exception when any of the passed listeners is null or when the entire vararg array is null.
@@ -312,7 +314,7 @@ public interface MockSettings extends Serializable {
MockSettings outerInstance(Object outerClassInstance);
/**
- * By default, Mockito makes an attempt to preserve all annotation meta data on the mocked
+ * By default, Mockito makes an attempt to preserve all annotation metadata on the mocked
* type and its methods to mirror the mocked type as closely as possible. If this is not
* desired, this option can be used to disable this behavior.
*
@@ -381,4 +383,32 @@ public interface MockSettings extends Serializable {
* @since 4.6.0
*/
MockSettings strictness(Strictness strictness);
+
+ /**
+ * Specifies the {@code MockMaker} for the mock.
+ * The default depends on your project as described in the class documentation of {@link MockMaker}.
+ * You should usually use the default, this option primarily exists to ease migrations.
+ * You may specify either one of the constants from {@link MockMakers},
+ *
+ * Object mock = Mockito.mock(Object.class, Mockito.withSettings()
+ * .mockMaker(MockMakers.INLINE));
+ *
+ * or the {@link Class#getName() binary name} of a class which implements the {@code MockMaker} interface.
+ *
+ * Object mock = Mockito.mock(Object.class, Mockito.withSettings()
+ * .mockMaker("org.awesome.mockito.AwesomeMockMaker"));
+ *
+ *
+ * @param mockMaker the {@code MockMaker} to use for the mock
+ * @return settings instance so that you can fluently specify other settings
+ * @since 4.8.0
+ */
+ MockSettings mockMaker(String mockMaker);
+
+ /**
+ * Specifies the generic type of the mock, preserving the information lost to Java type erasure.
+ * @param genericTypeToMock
+ * @return
+ */
+ MockSettings genericTypeToMock(Type genericTypeToMock);
}
diff --git a/src/main/java/org/mockito/MockedConstruction.java b/src/main/java/org/mockito/MockedConstruction.java
index 5c2ec2128d..ccf78c73a2 100644
--- a/src/main/java/org/mockito/MockedConstruction.java
+++ b/src/main/java/org/mockito/MockedConstruction.java
@@ -21,19 +21,52 @@
*/
public interface MockedConstruction extends ScopedMock {
+ /**
+ * Get the constructed mocks.
+ *
+ * @return the constructed mocks.
+ */
List constructed();
+ /**
+ * The context for a construction mock.
+ */
interface Context {
int getCount();
+ /**
+ * Get the constructor that is invoked during the mock creation.
+ *
+ * @return the constructor.
+ */
Constructor> constructor();
+ /**
+ * Get the arguments that were passed to the constructor.
+ *
+ * @return the arguments passed to the constructor, as a list.
+ */
List> arguments();
}
+ /**
+ * Functional interface that consumes a newly created mock and the mock context.
+ *
+ * Used to attach behaviours to new mocks.
+ *
+ * @param the mock type.
+ */
+ @FunctionalInterface
interface MockInitializer {
+ /**
+ * Configure the mock.
+ *
+ * @param mock the newly created mock.
+ * @param context the mock context.
+ * @throws Throwable any exception that may be thrown.
+ */
void prepare(T mock, Context context) throws Throwable;
}
}
diff --git a/src/main/java/org/mockito/MockedStatic.java b/src/main/java/org/mockito/MockedStatic.java
index 9095556fd7..113bee2e3a 100644
--- a/src/main/java/org/mockito/MockedStatic.java
+++ b/src/main/java/org/mockito/MockedStatic.java
@@ -62,8 +62,17 @@ default void verify(Verification verification) {
*/
void verifyNoInteractions();
+ /**
+ * Functional interface for a verification operation on a static mock.
+ */
+ @FunctionalInterface
interface Verification {
+ /**
+ * Apply the verification operation.
+ *
+ * @throws Throwable any exception that may be thrown.
+ */
void apply() throws Throwable;
}
}
diff --git a/src/main/java/org/mockito/MockingDetails.java b/src/main/java/org/mockito/MockingDetails.java
index c32e04e5c9..37a149100a 100644
--- a/src/main/java/org/mockito/MockingDetails.java
+++ b/src/main/java/org/mockito/MockingDetails.java
@@ -73,7 +73,7 @@ public interface MockingDetails {
* What is 'stubbing'?
* Stubbing is your when(x).then(y) declaration, e.g. configuring the mock to behave in a specific way,
* when specific method with specific arguments is invoked on a mock.
- * Typically stubbing is configuring mock to return X when method Y is invoked.
+ * Typically, stubbing is configuring mock to return X when method Y is invoked.
*
* Why do you need to access stubbings of a mock?
* In a normal workflow of creation clean tests, there is no need for this API.
diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java
index 9f7383f232..1fce63e51e 100644
--- a/src/main/java/org/mockito/Mockito.java
+++ b/src/main/java/org/mockito/Mockito.java
@@ -33,8 +33,13 @@
import org.mockito.stubbing.OngoingStubbing;
import org.mockito.stubbing.Stubber;
import org.mockito.stubbing.VoidAnswer1;
-import org.mockito.verification.*;
+import org.mockito.verification.After;
+import org.mockito.verification.Timeout;
+import org.mockito.verification.VerificationAfterDelay;
+import org.mockito.verification.VerificationMode;
+import org.mockito.verification.VerificationWithTimeout;
+import java.util.function.Consumer;
import java.util.function.Function;
/**
@@ -67,7 +72,7 @@
* 11. Stubbing with callbacks
* 12. doReturn()
|doThrow()
|doAnswer()
|doNothing()
|doCallRealMethod()
family of methods
* 13. Spying on real objects
- * 14. Changing default return values of unstubbed invocations (Since 1.7)
+ * 14. Changing default return values of un-stubbed invocations (Since 1.7)
* 15. Capturing arguments for further assertions (Since 1.8.0)
* 16. Real partial mocks (Since 1.8.0)
* 17. Resetting mocks (Since 1.8.0)
@@ -105,7 +110,9 @@
* 49. New API for mocking object construction (Since 3.5.0)
* 50. Avoiding code generation when restricting mocks to interfaces (Since 3.12.2)
* 51. New API for marking classes as unmockable (Since 4.1.0)
- * 52. New strictness attribute for @Mock annotation and MockSettings.strictness()
methods (Since 4.6.0)
+ * 52. New strictness attribute for @Mock annotation and MockSettings.strictness()
methods (Since 4.6.0)
+ * 53. Specifying mock maker for individual mocks (Since 4.8.0)
+ * 54. Mocking/spying without specifying class (Since 4.9.0)
*
*
*
@@ -220,7 +227,7 @@
*
* Stubbing can be overridden: for example common stubbing can go to
* fixture setup but the test methods can override it.
- * Please note that overridding stubbing is a potential code smell that points out too much stubbing
+ * Please note that overriding stubbing is a potential code smell that points out too much stubbing
*
* Once stubbed, the method will always return a stubbed value, regardless
* of how many times it is called.
@@ -646,7 +653,7 @@
* Mockito *does not* delegate calls to the passed real instance, instead it actually creates a copy of it.
* So if you keep the real instance and interact with it, don't expect the spied to be aware of those interaction
* and their effect on real instance state.
- * The corollary is that when an *unstubbed* method is called *on the spy* but *not on the real instance* ,
+ * The corollary is that when an *un-stubbed* method is called *on the spy* but *not on the real instance* ,
* you won't see any effects on the real instance.
*
*
@@ -659,7 +666,7 @@
*
*
*
- *
+ *
*
* You can create a mock with specified strategy for its return values.
* It's quite an advanced feature and typically you don't need it to write decent tests.
@@ -1586,7 +1593,7 @@
* released. To define mock behavior and to verify method invocations, use the MockedConstruction
that is returned.
*
*
- *
+ *
*
* The JVM offers the {@link java.lang.reflect.Proxy} facility for creating dynamic proxies of interface types. For most applications, Mockito
* must be capable of mocking classes as supported by the default mock maker, or even final classes, as supported by the inline mock maker. To
@@ -1609,7 +1616,7 @@
*
*
*
+ * New strictness attribute for @Mock annotation and MockSettings.strictness()
methods (Since 4.6.0)
*
* You can now customize the strictness level for a single mock, either using `@Mock` annotation strictness attribute or
* using `MockSettings.strictness()`. This can be useful if you want all of your mocks to be strict,
@@ -1622,6 +1629,52 @@
* Foo mock = Mockito.mock(Foo.class, withSettings().strictness(Strictness.WARN));
*
*
+ *
+ *
+ * You may encounter situations where you want to use a different mock maker for a specific test only.
+ * For example, you might want to migrate to the inline mock maker , but a few test do not work right away.
+ * In such case, you can (temporarily) use {@link MockSettings#mockMaker(String)} and {@link Mock#mockMaker()}
+ * to specify the mock maker for a specific mock which is causing the problem.
+ *
+ *
+ * // using annotation
+ * @Mock(mockMaker = MockMakers.SUBCLASS)
+ * Foo mock;
+ * // using MockSettings.withSettings()
+ * Foo mock = Mockito.mock(Foo.class, withSettings().mockMaker(MockMakers.SUBCLASS));
+ *
+ *
+ *
+ *
+ * Instead of calling method {@link Mockito#mock(Class)} or {@link Mockito#spy(Class)} with Class parameter, you can now
+ * now call method {@code mock()} or {@code spy()} without parameters :
+ *
+ *
+ * Foo foo = Mockito.mock();
+ * Bar bar = Mockito.spy();
+ *
+ *
+ * Mockito will automatically detect the needed class.
+ *
+ * It works only if you assign result of {@code mock()} or {@code spy()} to a variable or field with an explicit type.
+ * With an implicit type, the Java compiler is unable to automatically determine the type of a mock and you need
+ * to pass in the {@code Class} explicitly.
+ *
+ *
+ *
+ *
+ * To validate arguments during verification, instead of capturing them with {@link ArgumentCaptor}, you can now
+ * use {@link ArgumentMatchers#assertArg(Consumer)}}:
+ *
+ *
+ * verify(serviceMock).doStuff(assertArg(param -> {
+ * assertThat(param.getField1()).isEqualTo("foo");
+ * assertThat(param.getField2()).isEqualTo("bar");
+ * }));
+ *
*/
@CheckReturnValue
@SuppressWarnings("unchecked")
@@ -1632,9 +1685,9 @@ public class Mockito extends ArgumentMatchers {
/**
* The default Answer
of every mock if the mock was not stubbed.
*
- * Typically it just returns some empty value.
+ * Typically, it just returns some empty value.
*
- * {@link Answer} can be used to define the return values of unstubbed invocations.
+ * {@link Answer} can be used to define the return values of un-stubbed invocations.
*
* This implementation first tries the global configuration and if there is no global configuration then
* it will use a default answer that returns zeros, empty collections, nulls, etc.
@@ -1644,12 +1697,14 @@ 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.
+ * {@link Answer} can be used to define the return values of un-stubbed invocations.
*
* This implementation can be helpful when working with legacy code.
- * Unstubbed methods often return null. If your code uses the object returned by an unstubbed call you get a NullPointerException.
+ * Un-stubbed methods often return null. If your code uses the object returned by an un-stubbed call,
+ * you get a NullPointerException.
* This implementation of Answer returns SmartNull instead of null .
- * SmartNull
gives nicer exception message than NPE because it points out the line where unstubbed method was called. You just click on the stack trace.
+ * SmartNull
gives nicer exception messages than NPEs, because it points out the
+ * line where the un-stubbed method was called. You just click on the stack trace.
*
* ReturnsSmartNulls
first tries to return ordinary values (zeros, empty collections, empty string, etc.)
* then it tries to return SmartNull. If the return type is final then plain null
is returned.
@@ -1658,15 +1713,15 @@ public class Mockito extends ArgumentMatchers {
*
* Foo mock = mock(Foo.class, RETURNS_SMART_NULLS);
*
- * //calling unstubbed method here:
+ * //calling un-stubbed method here:
* Stuff stuff = mock.getStuff();
*
- * //using object returned by unstubbed call:
+ * //using object returned by un-stubbed call:
* stuff.doSomething();
*
* //Above doesn't yield NullPointerException this time!
* //Instead, SmartNullPointerException is thrown.
- * //Exception's cause links to unstubbed mock.getStuff() - just click on the stack trace.
+ * //Exception's cause links to un-stubbed mock.getStuff() - just click on the stack trace.
*
*/
public static final Answer RETURNS_SMART_NULLS = Answers.RETURNS_SMART_NULLS;
@@ -1674,7 +1729,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.
+ * {@link Answer} can be used to define the return values of un-stubbed invocations.
*
* This implementation can be helpful when working with legacy code.
*
@@ -1775,14 +1830,14 @@ 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.
+ * {@link Answer} can be used to define the return values of un-stubbed invocations.
*
* This implementation can be helpful when working with legacy code.
- * When this implementation is used, unstubbed methods will delegate to the real implementation.
+ * When this implementation is used, un-stubbed methods will delegate to the real implementation.
* This is a way to create a partial mock object that calls real methods by default.
*
- * As usual you are going to read the partial mock warning :
- * Object oriented programming is more less tackling complexity by dividing the complexity into separate, specific, SRPy objects.
+ * As usual, you are going to read the partial mock warning :
+ * Object oriented programming is more-or-less tackling complexity by dividing the complexity into separate, specific, SRPy objects.
* How does partial mock fit into this paradigm? Well, it just doesn't...
* Partial mock usually means that the complexity has been moved to a different method on the same object.
* In most cases, this is not the way you want to design your application.
@@ -1884,6 +1939,71 @@ public class Mockito extends ArgumentMatchers {
*/
public static final Answer RETURNS_SELF = Answers.RETURNS_SELF;
+ /**
+ * Creates a mock object of the requested class or interface.
+ *
+ * See examples in javadoc for the {@link Mockito} class.
+ *
+ * @param reified don't pass any values to it. It's a trick to detect the class/interface you
+ * want to mock.
+ * @return the mock object.
+ * @since 4.9.0
+ */
+ @SafeVarargs
+ public static T mock(T... reified) {
+ return mock(withSettings(), reified);
+ }
+
+ /**
+ * Creates a mock object of the requested class or interface with the given default answer.
+ *
+ * See examples in javadoc for the {@link Mockito} class.
+ *
+ * @param defaultAnswer the default answer to use.
+ * @param reified don't pass any values to it. It's a trick to detect the class/interface you
+ * want to mock.
+ * @return the mock object.
+ */
+ @SafeVarargs
+ public static T mock(@SuppressWarnings("rawtypes") Answer defaultAnswer, T... reified) {
+ return mock(withSettings().defaultAnswer(defaultAnswer), reified);
+ }
+
+ /**
+ * Creates a mock object of the requested class or interface with the given name.
+ *
+ * See examples in javadoc for the {@link Mockito} class.
+ *
+ * @param name the mock name to use.
+ * @param reified don't pass any values to it. It's a trick to detect the class/interface you
+ * want to mock.
+ * @return the mock object.
+ */
+ @SafeVarargs
+ public static T mock(String name, T... reified) {
+ return mock(withSettings().name(name).defaultAnswer(RETURNS_DEFAULTS), reified);
+ }
+
+ /**
+ * Creates a mock object of the requested class or interface with the given settings.
+ *
+ * See examples in javadoc for the {@link Mockito} class.
+ *
+ * @param settings the mock settings to use.
+ * @param reified don't pass any values to it. It's a trick to detect the class/interface you
+ * want to mock.
+ * @return the mock object.
+ */
+ @SafeVarargs
+ public static T mock(MockSettings settings, T... reified) {
+ if (reified == null || reified.length > 0) {
+ throw new IllegalArgumentException(
+ "Please don't pass any values here. Java will detect class automagically.");
+ }
+
+ return mock(getClassOf(reified), settings);
+ }
+
/**
* Creates mock object of given class or interface.
*
@@ -1946,7 +2066,7 @@ public static MockingDetails mockingDetails(Object toInspect) {
*
See examples in javadoc for {@link Mockito} class
*
* @param classToMock class or interface to mock
- * @param defaultAnswer default answer for unstubbed methods
+ * @param defaultAnswer default answer for un-stubbed methods
*
* @return mock object
*/
@@ -1957,7 +2077,7 @@ public static T mock(Class classToMock, Answer defaultAnswer) {
/**
* Creates a mock with some non-standard settings.
*
- * The number of configuration points for a mock grows
+ * The number of configuration points for a mock will grow,
* so we need a fluent way to introduce new configuration without adding more and more overloaded Mockito.mock() methods.
* Hence {@link MockSettings}.
*
@@ -1967,7 +2087,7 @@ public static T mock(Class classToMock, Answer defaultAnswer) {
*
* Use it carefully and occasionally . What might be reason your test needs non-standard mocks?
* Is the code under test so complicated that it requires non-standard mocks?
- * Wouldn't you prefer to refactor the code under test so it is testable in a simple way?
+ * Wouldn't you prefer to refactor the code under test, so that it is testable in a simple way?
*
* See also {@link Mockito#withSettings()}
*
@@ -1986,7 +2106,7 @@ public static T mock(Class classToMock, MockSettings mockSettings) {
*
* Real spies should be used carefully and occasionally , for example when dealing with legacy code.
*
- * As usual you are going to read the partial mock warning :
+ * As usual, you are going to read the partial mock warning :
* Object oriented programming tackles complexity by dividing the complexity into separate, specific, SRPy objects.
* How does partial mock fit into this paradigm? Well, it just doesn't...
* Partial mock usually means that the complexity has been moved to a different method on the same object.
@@ -2041,7 +2161,7 @@ public static T mock(Class classToMock, MockSettings mockSettings) {
* Mockito *does not* delegate calls to the passed real instance, instead it actually creates a copy of it.
* So if you keep the real instance and interact with it, don't expect the spied to be aware of those interaction
* and their effect on real instance state.
- * The corollary is that when an *unstubbed* method is called *on the spy* but *not on the real instance* ,
+ * The corollary is that when an *un-stubbed* method is called *on the spy* but *not on the real instance* ,
* you won't see any effects on the real instance.
*
* Watch out for final methods.
@@ -2098,13 +2218,33 @@ public static T spy(Class classToSpy) {
classToSpy, withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS));
}
+ /**
+ * Please refer to the documentation of {@link #spy(Class)}.
+ *
+ * @param reified don't pass any values to it. It's a trick to detect the class/interface you want to mock.
+ * @return spy object
+ * @since 4.9.0
+ */
+ @SafeVarargs
+ public static T spy(T... reified) {
+ if (reified.length > 0) {
+ throw new IllegalArgumentException(
+ "Please don't pass any values here. Java will detect class automagically.");
+ }
+ return spy(getClassOf(reified));
+ }
+
+ private static Class getClassOf(T[] array) {
+ return (Class) array.getClass().getComponentType();
+ }
+
/**
* Creates a thread-local mock controller for all static methods of the given class or interface.
* The returned object's {@link MockedStatic#close()} method must be called upon completing the
* test or the mock will remain active on the current thread.
*
* Note : We recommend against mocking static methods of classes in the standard library or
- * classes used by custom class loaders used to executed the block with the mocked class. A mock
+ * classes used by custom class loaders used to execute the block with the mocked class. A mock
* maker might forbid mocking static methods of know classes that are known to cause problems.
* Also, if a static method is a JVM-intrinsic, it cannot typically be mocked even if not
* explicitly forbidden.
@@ -2124,7 +2264,7 @@ public static MockedStatic mockStatic(Class classToMock) {
* test or the mock will remain active on the current thread.
*
* Note : We recommend against mocking static methods of classes in the standard library or
- * classes used by custom class loaders used to executed the block with the mocked class. A mock
+ * classes used by custom class loaders used to execute the block with the mocked class. A mock
* maker might forbid mocking static methods of know classes that are known to cause problems.
* Also, if a static method is a JVM-intrinsic, it cannot typically be mocked even if not
* explicitly forbidden.
@@ -2145,7 +2285,7 @@ public static MockedStatic mockStatic(Class classToMock, Answer defaul
* test or the mock will remain active on the current thread.
*
* Note : We recommend against mocking static methods of classes in the standard library or
- * classes used by custom class loaders used to executed the block with the mocked class. A mock
+ * classes used by custom class loaders used to execute the block with the mocked class. A mock
* maker might forbid mocking static methods of know classes that are known to cause problems.
* Also, if a static method is a JVM-intrinsic, it cannot typically be mocked even if not
* explicitly forbidden.
@@ -2166,7 +2306,7 @@ public static MockedStatic mockStatic(Class classToMock, String name)
* test or the mock will remain active on the current thread.
*
* Note : We recommend against mocking static methods of classes in the standard library or
- * classes used by custom class loaders used to executed the block with the mocked class. A mock
+ * classes used by custom class loaders used to execute the block with the mocked class. A mock
* maker might forbid mocking static methods of know classes that are known to cause problems.
* Also, if a static method is a JVM-intrinsic, it cannot typically be mocked even if not
* explicitly forbidden.
@@ -2234,7 +2374,7 @@ public static MockedConstruction mockConstruction(Class classToMock) {
* See examples in javadoc for {@link Mockito} class
*
* @param classToMock non-abstract class of which constructions should be mocked.
- * @param mockInitializer a callback to prepare a mock's methods after its instantiation.
+ * @param mockInitializer a callback to prepare the methods on a mock after its instantiation.
* @return mock controller
*/
public static MockedConstruction mockConstruction(
@@ -2284,7 +2424,7 @@ public static MockedConstruction mockConstruction(
*
* @param classToMock non-abstract class of which constructions should be mocked.
* @param mockSettings the settings to use.
- * @param mockInitializer a callback to prepare a mock's methods after its instantiation.
+ * @param mockInitializer a callback to prepare the methods on a mock after its instantiation.
* @return mock controller
*/
public static MockedConstruction mockConstruction(
@@ -2303,7 +2443,7 @@ public static MockedConstruction mockConstruction(
*
* @param classToMock non-abstract class of which constructions should be mocked.
* @param mockSettingsFactory a function to create settings to use.
- * @param mockInitializer a callback to prepare a mock's methods after its instantiation.
+ * @param mockInitializer a callback to prepare the methods on a mock after its instantiation.
* @return mock controller
*/
public static MockedConstruction mockConstruction(
@@ -2354,7 +2494,7 @@ public static MockedConstruction mockConstruction(
*
* Stubbing can be overridden: for example common stubbing can go to fixture
* setup but the test methods can override it.
- * Please note that overridding stubbing is a potential code smell that points out too much stubbing.
+ * Please note that overriding stubbing is a potential code smell that points out too much stubbing.
*
* Once stubbed, the method will always return stubbed value regardless
* of how many times it is called.
@@ -2506,7 +2646,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.
+ * Abusing it leads to over-specified, 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.
@@ -2599,7 +2739,8 @@ public static Stubber doThrow(Class extends Throwable> toBeThrown) {
/**
* Same as {@link #doThrow(Class)} but sets consecutive exception classes to be thrown. Remember to use
- * doThrow()
when you want to stub the void method to throw several exception of specified class.
+ * doThrow()
when you want to stub the void method to throw several exceptions
+ * that are instances of the specified class.
*
* A new exception instance will be created for each method invocation.
*
@@ -2628,8 +2769,8 @@ public static Stubber doThrow(
/**
* Use doCallRealMethod()
when you want to call the real implementation of a method.
*
- * As usual you are going to read the partial mock warning :
- * Object oriented programming is more less tackling complexity by dividing the complexity into separate, specific, SRPy objects.
+ * As usual, you are going to read the partial mock warning :
+ * Object oriented programming is more-or-less tackling complexity by dividing the complexity into separate, specific, SRPy objects.
* How does partial mock fit into this paradigm? Well, it just doesn't...
* Partial mock usually means that the complexity has been moved to a different method on the same object.
* In most cases, this is not the way you want to design your application.
@@ -2767,7 +2908,7 @@ public static Stubber doNothing() {
*
* Above scenarios shows a tradeoff of Mockito's elegant syntax. Note that the scenarios are very rare, though.
* Spying should be sporadic and overriding exception-stubbing is very rare. Not to mention that in general
- * overridding stubbing is a potential code smell that points out too much stubbing.
+ * overriding stubbing is a potential code smell that points out too much stubbing.
*
* See examples in javadoc for {@link Mockito} class
*
@@ -2818,7 +2959,7 @@ public static Stubber doReturn(Object toBeReturned) {
*
* Above scenarios shows a trade-off of Mockito's elegant syntax. Note that the scenarios are very rare, though.
* Spying should be sporadic and overriding exception-stubbing is very rare. Not to mention that in general
- * overridding stubbing is a potential code smell that points out too much stubbing.
+ * overriding stubbing is a potential code smell that points out too much stubbing.
*
* See examples in javadoc for {@link Mockito} class
*
@@ -2869,7 +3010,7 @@ public static InOrder inOrder(Object... mocks) {
* and provides other benefits.
*
* ignoreStubs()
is sometimes useful when coupled with verifyNoMoreInteractions()
or verification inOrder()
.
- * Helps avoiding redundant verification of stubbed calls - typically we're not interested in verifying stubs.
+ * Helps to avoid redundant verification of stubbed calls - typically we're not interested in verifying stubs.
*
* Warning , ignoreStubs()
might lead to overuse of verifyNoMoreInteractions(ignoreStubs(...));
* Bear in mind that Mockito does not recommend bombarding every test with verifyNoMoreInteractions()
@@ -3116,7 +3257,7 @@ public static VerificationWithTimeout timeout(long millis) {
/**
* Verification will be triggered after given amount of millis, allowing testing of async code.
- * Useful when interactions with the mock object did not happened yet.
+ * Useful when interactions with the mock object have yet to occur.
* Extensive use of {@code after()} method can be a code smell - there are better ways of testing concurrent code.
*
* Not yet implemented to work with InOrder verification.
@@ -3276,7 +3417,7 @@ public static MockitoFramework framework() {
/**
* {@code MockitoSession} is an optional, highly recommended feature
- * that helps driving cleaner tests by eliminating boilerplate code and adding extra validation.
+ * that drives writing cleaner tests by eliminating boilerplate code and adding extra validation.
*
* For more information, including use cases and sample code, see the javadoc for {@link MockitoSession}.
*
@@ -3298,7 +3439,7 @@ public static MockitoSessionBuilder mockitoSession() {
* Most mocks in most tests don't need leniency and should happily prosper with {@link Strictness#STRICT_STUBS}.
*
* If a specific stubbing needs to be lenient - use this method
- * If a specific mock need to have stubbings lenient - use {@link MockSettings#lenient()}
+ * If a specific mock need to have lenient stubbings - use {@link MockSettings#strictness(Strictness)}
* If a specific test method / test class needs to have all stubbings lenient
* - configure strictness using our JUnit support ({@link MockitoJUnit} or Mockito Session ({@link MockitoSession})
*
diff --git a/src/main/java/org/mockito/MockitoFramework.java b/src/main/java/org/mockito/MockitoFramework.java
index ba814e6029..020186f052 100644
--- a/src/main/java/org/mockito/MockitoFramework.java
+++ b/src/main/java/org/mockito/MockitoFramework.java
@@ -26,11 +26,11 @@ public interface MockitoFramework {
* Adds listener to Mockito.
* For a list of supported listeners, see the interfaces that extend {@link MockitoListener}.
*
- * Listeners can be useful for engs that extend Mockito framework.
+ * Listeners can be useful for components that extend Mockito framework.
* They are used in the implementation of unused stubbings warnings ({@link org.mockito.quality.MockitoHint}).
*
* Make sure you remove the listener when the job is complete, see {@link #removeListener(MockitoListener)}.
- * Currently the listeners list is thread local so you need to remove listener from the same thread otherwise
+ * Currently, the listeners list is thread local, so you need to remove listener from the same thread otherwise
* remove is ineffectual.
* In typical scenarios, it is not a problem, because adding and removing listeners typically happens in the same thread.
*
@@ -56,7 +56,7 @@ public interface MockitoFramework {
/**
* When you add listener using {@link #addListener(MockitoListener)} make sure to remove it.
- * Currently the listeners list is thread local so you need to remove listener from the same thread otherwise
+ * Currently, the listeners list is thread local, so you need to remove listener from the same thread otherwise
* remove is ineffectual.
* In typical scenarios, it is not a problem, because adding and removing listeners typically happens in the same thread.
*
@@ -92,7 +92,7 @@ public interface MockitoFramework {
/**
* Clears up internal state of all inline mocks.
* This method is only meaningful if inline mock maker is in use.
- * Otherwise this method is a no-op and need not be used.
+ * For all other intents and purposes, this method is a no-op and need not be used.
*
* This method is useful to tackle subtle memory leaks that are possible due to the nature of inline mocking
* (issue #1619 ).
diff --git a/src/main/java/org/mockito/MockitoSession.java b/src/main/java/org/mockito/MockitoSession.java
index d3ad832c67..f87e07dcd4 100644
--- a/src/main/java/org/mockito/MockitoSession.java
+++ b/src/main/java/org/mockito/MockitoSession.java
@@ -17,12 +17,12 @@
/**
* {@code MockitoSession} is an optional, highly recommended feature
- * that helps driving cleaner tests by eliminating boilerplate code and adding extra validation.
+ * that drives writing cleaner tests by eliminating boilerplate code and adding extra validation.
* If you already use {@link MockitoJUnitRunner} or {@link MockitoRule}
* *you don't need* {@code MockitoSession} because it is used by the runner/rule.
*
* {@code MockitoSession} is a session of mocking, during which the user creates and uses Mockito mocks.
- * Typically the session is an execution of a single test method.
+ * Typically, the session is an execution of a single test method.
* {@code MockitoSession} initializes mocks, validates usage and detects incorrect stubbing.
* When the session is started it must be concluded with {@link #finishMocking()}
* otherwise {@link UnfinishedMockingSessionException} is triggered when the next session is created.
diff --git a/src/main/java/org/mockito/NotExtensible.java b/src/main/java/org/mockito/NotExtensible.java
index 39191ac481..f5110f5d57 100644
--- a/src/main/java/org/mockito/NotExtensible.java
+++ b/src/main/java/org/mockito/NotExtensible.java
@@ -19,7 +19,7 @@
* Public types are all types that are *not* under "org.mockito.internal.*" package.
*
* Absence of {@code NotExtensible} annotation on a type *does not* mean it is intended to be extended.
- * The annotation has been introduced late and therefore it is not used frequently in the codebase.
+ * The annotation has been introduced late, and therefore it is not used frequently in the codebase.
* Many public types from Mockito API are not intended for extension, even though they do not have this annotation applied.
*
* @since 2.10.0
diff --git a/src/main/java/org/mockito/Spy.java b/src/main/java/org/mockito/Spy.java
index aa485a355d..fa5e6e5ad0 100644
--- a/src/main/java/org/mockito/Spy.java
+++ b/src/main/java/org/mockito/Spy.java
@@ -14,7 +14,7 @@
import org.mockito.junit.MockitoJUnitRunner;
/**
- * Allows shorthand wrapping of field instances in an spy object.
+ * Allows shorthand wrapping of field instances in a spy object.
*
*
* Example:
@@ -79,7 +79,7 @@
*
Mockito *does not* delegate calls to the passed real instance, instead it actually creates a copy of it.
* So if you keep the real instance and interact with it, don't expect the spied to be aware of those interaction
* and their effect on real instance state.
- * The corollary is that when an *unstubbed* method is called *on the spy* but *not on the real instance* ,
+ * The corollary is that when an *un-stubbed* method is called *on the spy* but *not on the real instance* ,
* you won't see any effects on the real instance.
*
* Watch out for final methods.
diff --git a/src/main/java/org/mockito/configuration/IMockitoConfiguration.java b/src/main/java/org/mockito/configuration/IMockitoConfiguration.java
index 53664940ea..6e16d350f1 100644
--- a/src/main/java/org/mockito/configuration/IMockitoConfiguration.java
+++ b/src/main/java/org/mockito/configuration/IMockitoConfiguration.java
@@ -13,7 +13,7 @@
* In most cases you don't really need to configure Mockito. For example in case of working with legacy code,
* when you might want to have different 'mocking style' this interface might be helpful.
* A reason of configuring Mockito might be if you disagree with the {@link org.mockito.Answers#RETURNS_DEFAULTS}
- * unstubbed mocks return.
+ * un-stubbed mocks return.
*
*
* To configure Mockito create exactly org.mockito.configuration.MockitoConfiguration class
@@ -38,7 +38,7 @@
public interface IMockitoConfiguration {
/**
- * Allows configuring the default answers of unstubbed invocations
+ * Allows configuring the default answers of un-stubbed invocations
*
* See javadoc for {@link IMockitoConfiguration}
*/
diff --git a/src/main/java/org/mockito/exceptions/stacktrace/StackTraceCleaner.java b/src/main/java/org/mockito/exceptions/stacktrace/StackTraceCleaner.java
index a3229a8c80..467809aa01 100644
--- a/src/main/java/org/mockito/exceptions/stacktrace/StackTraceCleaner.java
+++ b/src/main/java/org/mockito/exceptions/stacktrace/StackTraceCleaner.java
@@ -25,4 +25,29 @@ public interface StackTraceCleaner {
* @return whether the element should be excluded from cleaned stack trace.
*/
boolean isIn(StackTraceElement candidate);
+
+ /**
+ * It's recommended to override this method in subclasses to avoid potentially costly re-boxing operations.
+ */
+ default boolean isIn(StackFrameMetadata candidate) {
+ return isIn(
+ new StackTraceElement(
+ candidate.getClassName(),
+ candidate.getMethodName(),
+ candidate.getFileName(),
+ candidate.getLineNumber()));
+ }
+
+ /**
+ * Very similar to the StackFrame class declared on the StackWalker api.
+ */
+ interface StackFrameMetadata {
+ String getClassName();
+
+ String getMethodName();
+
+ String getFileName();
+
+ int getLineNumber();
+ }
}
diff --git a/src/main/java/org/mockito/hamcrest/MockitoHamcrest.java b/src/main/java/org/mockito/hamcrest/MockitoHamcrest.java
index b8ebf8f97d..3624ba20c4 100644
--- a/src/main/java/org/mockito/hamcrest/MockitoHamcrest.java
+++ b/src/main/java/org/mockito/hamcrest/MockitoHamcrest.java
@@ -42,6 +42,24 @@
* Due to how java works we don't really have a clean way of detecting this scenario and protecting the user from this problem.
* Hopefully, the javadoc describes the problem and solution well.
* If you have an idea how to fix the problem, let us know via the mailing list or the issue tracker.
+ *
+ * By default, a matcher passed to a varargs parameter will match against the first element in the varargs array.
+ * To match against the raw varargs array pass the type of the varargs parameter to {@link MockitoHamcrest#argThat(Matcher, Class)}
+ *
+ * For example, to match any number of {@code String} values:
+ *
+ * import static org.hamcrest.CoreMatchers.isA;
+ * import static org.mockito.hamcrest.MockitoHamcrest.argThat;
+ *
+ * // Given:
+ * void varargMethod(String... args);
+ *
+ * //stubbing
+ * when(mock.varargMethod(argThat(isA(String[].class), String[].class));
+ *
+ * //verification
+ * verify(mock).giveMe(argThat(isA(String[].class), String[].class));
+ *
*
* @since 2.1.0
*/
@@ -62,6 +80,26 @@ public static T argThat(Matcher matcher) {
return (T) defaultValue(genericTypeOfMatcher(matcher.getClass()));
}
+ /**
+ * Allows matching arguments with hamcrest matchers.
+ *
+ * This variant can be used to pass an explicit {@code type},
+ * which can be useful to provide a matcher that matches against all
+ * elements in a varargs parameter.
+ *
+ * See examples in javadoc for {@link MockitoHamcrest} class
+ *
+ * @param matcher decides whether argument matches
+ * @param type the type the matcher matches.
+ * @return null
or default value for primitive (0, false, etc.)
+ * @since 5.0.0
+ */
+ @SuppressWarnings("unchecked")
+ public static T argThat(Matcher matcher, Class type) {
+ reportMatcher(matcher, type);
+ return (T) defaultValue(genericTypeOfMatcher(matcher.getClass()));
+ }
+
/**
* Enables integrating hamcrest matchers that match primitive char
arguments.
* Note that {@link #argThat} will not work with primitive char
matchers due to NullPointerException
auto-unboxing caveat.
@@ -175,9 +213,15 @@ public static double doubleThat(Matcher matcher) {
}
private static void reportMatcher(Matcher matcher) {
- mockingProgress()
- .getArgumentMatcherStorage()
- .reportMatcher(new HamcrestArgumentMatcher(matcher));
+ reportMatcher(new HamcrestArgumentMatcher(matcher));
+ }
+
+ private static void reportMatcher(Matcher matcher, Class type) {
+ reportMatcher(new HamcrestArgumentMatcher(matcher, type));
+ }
+
+ private static void reportMatcher(final HamcrestArgumentMatcher matcher) {
+ mockingProgress().getArgumentMatcherStorage().reportMatcher(matcher);
}
private MockitoHamcrest() {}
diff --git a/src/main/java/org/mockito/internal/MockedConstructionImpl.java b/src/main/java/org/mockito/internal/MockedConstructionImpl.java
index 47bd8089c6..5541ba07cd 100644
--- a/src/main/java/org/mockito/internal/MockedConstructionImpl.java
+++ b/src/main/java/org/mockito/internal/MockedConstructionImpl.java
@@ -11,7 +11,7 @@
import org.mockito.MockedConstruction;
import org.mockito.exceptions.base.MockitoException;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.debugging.LocationFactory;
import org.mockito.invocation.Location;
import org.mockito.plugins.MockMaker;
@@ -21,7 +21,7 @@ public final class MockedConstructionImpl implements MockedConstruction {
private boolean closed;
- private final Location location = new LocationImpl();
+ private final Location location = LocationFactory.create();
protected MockedConstructionImpl(MockMaker.ConstructionMockControl control) {
this.control = control;
diff --git a/src/main/java/org/mockito/internal/MockedStaticImpl.java b/src/main/java/org/mockito/internal/MockedStaticImpl.java
index fbfb54b004..f6705fb81a 100644
--- a/src/main/java/org/mockito/internal/MockedStaticImpl.java
+++ b/src/main/java/org/mockito/internal/MockedStaticImpl.java
@@ -17,7 +17,7 @@
import org.mockito.Mockito;
import org.mockito.exceptions.base.MockitoAssertionError;
import org.mockito.exceptions.base.MockitoException;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.debugging.LocationFactory;
import org.mockito.internal.listeners.VerificationStartedNotifier;
import org.mockito.internal.progress.MockingProgress;
import org.mockito.internal.stubbing.InvocationContainerImpl;
@@ -35,7 +35,7 @@ public final class MockedStaticImpl implements MockedStatic {
private boolean closed;
- private final Location location = new LocationImpl();
+ private final Location location = LocationFactory.create();
protected MockedStaticImpl(MockMaker.StaticMockControl control) {
this.control = control;
diff --git a/src/main/java/org/mockito/internal/MockitoCore.java b/src/main/java/org/mockito/internal/MockitoCore.java
index fff3b7667e..fd39f6a4a2 100644
--- a/src/main/java/org/mockito/internal/MockitoCore.java
+++ b/src/main/java/org/mockito/internal/MockitoCore.java
@@ -22,7 +22,6 @@
import static org.mockito.internal.util.MockUtil.getMockHandler;
import static org.mockito.internal.util.MockUtil.isMock;
import static org.mockito.internal.util.MockUtil.resetMock;
-import static org.mockito.internal.util.MockUtil.typeMockabilityOf;
import static org.mockito.internal.verification.VerificationModeFactory.noInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.noMoreInteractions;
@@ -31,6 +30,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Function;
import org.mockito.InOrder;
import org.mockito.MockSettings;
@@ -67,10 +67,6 @@
import org.mockito.stubbing.Stubber;
import org.mockito.verification.VerificationMode;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Function;
-
@SuppressWarnings("unchecked")
public class MockitoCore {
@@ -78,10 +74,6 @@ public class MockitoCore {
private static final Set> MOCKABLE_CLASSES =
Collections.synchronizedSet(new HashSet<>());
- public boolean isTypeMockable(Class> typeToMock) {
- return typeMockabilityOf(typeToMock).mockable();
- }
-
public T mock(Class typeToMock, MockSettings settings) {
if (!(settings instanceof MockSettingsImpl)) {
throw new IllegalArgumentException(
@@ -160,6 +152,14 @@ public MockedConstruction mockConstruction(
+ "At the moment, you cannot provide your own implementations of that class.");
}
MockSettingsImpl impl = MockSettingsImpl.class.cast(value);
+ String mockMaker = impl.getMockMaker();
+ if (mockMaker != null) {
+ throw new IllegalArgumentException(
+ "Unexpected MockMaker '"
+ + mockMaker
+ + "'\n"
+ + "At the moment, you cannot override the MockMaker for construction mocks.");
+ }
return impl.build(typeToMock);
};
MockMaker.ConstructionMockControl control =
diff --git a/src/main/java/org/mockito/internal/configuration/GlobalConfiguration.java b/src/main/java/org/mockito/internal/configuration/GlobalConfiguration.java
index 92f5a54eb4..d5ad75c93b 100644
--- a/src/main/java/org/mockito/internal/configuration/GlobalConfiguration.java
+++ b/src/main/java/org/mockito/internal/configuration/GlobalConfiguration.java
@@ -43,7 +43,7 @@ private IMockitoConfiguration createConfig() {
}
public static void validate() {
- new GlobalConfiguration();
+ GlobalConfiguration unused = new GlobalConfiguration();
}
public org.mockito.plugins.AnnotationEngine tryGetPluginAnnotationEngine() {
diff --git a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java
index 66c3f31d12..f61f4a7bd9 100644
--- a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java
+++ b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java
@@ -53,6 +53,14 @@ public static Object processAnnotationForMock(
if (annotation.strictness() != Mock.Strictness.TEST_LEVEL_DEFAULT) {
mockSettings.strictness(Strictness.valueOf(annotation.strictness().toString()));
}
+ if (!annotation.mockMaker().isEmpty()) {
+ mockSettings.mockMaker(annotation.mockMaker());
+ }
+ if (annotation.withoutAnnotations()) {
+ mockSettings.withoutAnnotations();
+ }
+
+ mockSettings.genericTypeToMock(genericType.get());
// see @Mock answer default value
mockSettings.defaultAnswer(annotation.answer());
diff --git a/src/main/java/org/mockito/internal/configuration/SpyAnnotationEngine.java b/src/main/java/org/mockito/internal/configuration/SpyAnnotationEngine.java
index a88ad63b84..cd51942581 100644
--- a/src/main/java/org/mockito/internal/configuration/SpyAnnotationEngine.java
+++ b/src/main/java/org/mockito/internal/configuration/SpyAnnotationEngine.java
@@ -83,9 +83,11 @@ public AutoCloseable process(Class> context, Object testInstance) {
}
private static Object spyInstance(Field field, Object instance) {
+ // TODO: Add mockMaker option for @Spy annotation (#2740)
return Mockito.mock(
instance.getClass(),
withSettings()
+ .genericTypeToMock(field.getGenericType())
.spiedInstance(instance)
.defaultAnswer(CALLS_REAL_METHODS)
.name(field.getName()));
@@ -93,8 +95,12 @@ private static Object spyInstance(Field field, Object instance) {
private static Object spyNewInstance(Object testInstance, Field field)
throws InstantiationException, IllegalAccessException, InvocationTargetException {
+ // TODO: Add mockMaker option for @Spy annotation (#2740)
MockSettings settings =
- withSettings().defaultAnswer(CALLS_REAL_METHODS).name(field.getName());
+ withSettings()
+ .genericTypeToMock(field.getGenericType())
+ .defaultAnswer(CALLS_REAL_METHODS)
+ .name(field.getName());
Class> type = field.getType();
if (type.isInterface()) {
return Mockito.mock(type, settings.useConstructor());
diff --git a/src/main/java/org/mockito/internal/configuration/injection/PropertyAndSetterInjection.java b/src/main/java/org/mockito/internal/configuration/injection/PropertyAndSetterInjection.java
index 7f267d2c5e..ed32c8eea4 100644
--- a/src/main/java/org/mockito/internal/configuration/injection/PropertyAndSetterInjection.java
+++ b/src/main/java/org/mockito/internal/configuration/injection/PropertyAndSetterInjection.java
@@ -81,6 +81,7 @@ public boolean processInjection(
injectMockCandidates(
fieldClass,
fieldInstanceNeedingInjection,
+ injectMocksField,
newMockSafeHashSet(mockCandidates));
fieldClass = fieldClass.getSuperclass();
}
@@ -100,24 +101,32 @@ private FieldInitializationReport initializeInjectMocksField(Field field, Object
}
private boolean injectMockCandidates(
- Class> awaitingInjectionClazz, Object injectee, Set mocks) {
+ Class> awaitingInjectionClazz,
+ Object injectee,
+ Field injectMocksField,
+ Set mocks) {
boolean injectionOccurred;
List orderedCandidateInjecteeFields =
orderedInstanceFieldsFrom(awaitingInjectionClazz);
// pass 1
injectionOccurred =
injectMockCandidatesOnFields(
- mocks, injectee, false, orderedCandidateInjecteeFields);
+ mocks, injectee, injectMocksField, false, orderedCandidateInjecteeFields);
// pass 2
injectionOccurred |=
injectMockCandidatesOnFields(
- mocks, injectee, injectionOccurred, orderedCandidateInjecteeFields);
+ mocks,
+ injectee,
+ injectMocksField,
+ injectionOccurred,
+ orderedCandidateInjecteeFields);
return injectionOccurred;
}
private boolean injectMockCandidatesOnFields(
Set mocks,
Object injectee,
+ Field injectMocksField,
boolean injectionOccurred,
List orderedCandidateInjecteeFields) {
for (Iterator it = orderedCandidateInjecteeFields.iterator(); it.hasNext(); ) {
@@ -125,7 +134,11 @@ private boolean injectMockCandidatesOnFields(
Object injected =
mockCandidateFilter
.filterCandidate(
- mocks, candidateField, orderedCandidateInjecteeFields, injectee)
+ mocks,
+ candidateField,
+ orderedCandidateInjecteeFields,
+ injectee,
+ injectMocksField)
.thenInject();
if (injected != null) {
injectionOccurred |= true;
diff --git a/src/main/java/org/mockito/internal/configuration/injection/SpyOnInjectedFieldsHandler.java b/src/main/java/org/mockito/internal/configuration/injection/SpyOnInjectedFieldsHandler.java
index 73f3004c19..bbfe88b85a 100644
--- a/src/main/java/org/mockito/internal/configuration/injection/SpyOnInjectedFieldsHandler.java
+++ b/src/main/java/org/mockito/internal/configuration/injection/SpyOnInjectedFieldsHandler.java
@@ -42,6 +42,7 @@ protected boolean processInjection(Field field, Object fieldOwner, Set m
// B. protect against multiple use of MockitoAnnotations.openMocks()
Mockito.reset(instance);
} else {
+ // TODO: Add mockMaker option for @Spy annotation (#2740)
Object mock =
Mockito.mock(
instance.getClass(),
diff --git a/src/main/java/org/mockito/internal/configuration/injection/filter/MockCandidateFilter.java b/src/main/java/org/mockito/internal/configuration/injection/filter/MockCandidateFilter.java
index 470a42ff17..82c913695e 100644
--- a/src/main/java/org/mockito/internal/configuration/injection/filter/MockCandidateFilter.java
+++ b/src/main/java/org/mockito/internal/configuration/injection/filter/MockCandidateFilter.java
@@ -13,5 +13,6 @@ OngoingInjector filterCandidate(
Collection mocks,
Field candidateFieldToBeInjected,
List allRemainingCandidateFields,
- Object injectee);
+ Object injectee,
+ Field injectMocksField);
}
diff --git a/src/main/java/org/mockito/internal/configuration/injection/filter/NameBasedCandidateFilter.java b/src/main/java/org/mockito/internal/configuration/injection/filter/NameBasedCandidateFilter.java
index 125b9595ef..b2cc6c75d5 100644
--- a/src/main/java/org/mockito/internal/configuration/injection/filter/NameBasedCandidateFilter.java
+++ b/src/main/java/org/mockito/internal/configuration/injection/filter/NameBasedCandidateFilter.java
@@ -23,7 +23,8 @@ public OngoingInjector filterCandidate(
final Collection mocks,
final Field candidateFieldToBeInjected,
final List allRemainingCandidateFields,
- final Object injectee) {
+ final Object injectee,
+ final Field injectMocksField) {
if (mocks.size() == 1
&& anotherCandidateMatchesMockName(
mocks, candidateFieldToBeInjected, allRemainingCandidateFields)) {
@@ -34,7 +35,8 @@ && anotherCandidateMatchesMockName(
tooMany(mocks) ? selectMatchingName(mocks, candidateFieldToBeInjected) : mocks,
candidateFieldToBeInjected,
allRemainingCandidateFields,
- injectee);
+ injectee,
+ injectMocksField);
}
private boolean tooMany(Collection mocks) {
diff --git a/src/main/java/org/mockito/internal/configuration/injection/filter/TerminalMockCandidateFilter.java b/src/main/java/org/mockito/internal/configuration/injection/filter/TerminalMockCandidateFilter.java
index ec9dadcfa6..5726ee2015 100644
--- a/src/main/java/org/mockito/internal/configuration/injection/filter/TerminalMockCandidateFilter.java
+++ b/src/main/java/org/mockito/internal/configuration/injection/filter/TerminalMockCandidateFilter.java
@@ -28,7 +28,8 @@ public OngoingInjector filterCandidate(
final Collection mocks,
final Field candidateFieldToBeInjected,
final List allRemainingCandidateFields,
- final Object injectee) {
+ final Object injectee,
+ final Field injectMocksField) {
if (mocks.size() == 1) {
final Object matchingMock = mocks.iterator().next();
diff --git a/src/main/java/org/mockito/internal/configuration/injection/filter/TypeBasedCandidateFilter.java b/src/main/java/org/mockito/internal/configuration/injection/filter/TypeBasedCandidateFilter.java
index 9ba0fba986..a66ab52144 100644
--- a/src/main/java/org/mockito/internal/configuration/injection/filter/TypeBasedCandidateFilter.java
+++ b/src/main/java/org/mockito/internal/configuration/injection/filter/TypeBasedCandidateFilter.java
@@ -4,10 +4,20 @@
*/
package org.mockito.internal.configuration.injection.filter;
+import static org.mockito.internal.exceptions.Reporter.moreThanOneMockCandidate;
+
import java.lang.reflect.Field;
+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.Collection;
import java.util.List;
+import java.util.stream.Stream;
+
+import org.mockito.internal.util.MockUtil;
public class TypeBasedCandidateFilter implements MockCandidateFilter {
@@ -17,20 +27,169 @@ public TypeBasedCandidateFilter(MockCandidateFilter next) {
this.next = next;
}
+ protected boolean isCompatibleTypes(Type typeToMock, Type mockType, Field injectMocksField) {
+ boolean result = false;
+ if (typeToMock instanceof ParameterizedType) {
+ if (mockType instanceof ParameterizedType) {
+ // ParameterizedType.equals() is documented as:
+ // "Instances of classes that implement this interface must implement
+ // an equals() method that equates any two instances that share the
+ // same generic type declaration and have equal type parameters."
+ // Unfortunately, e.g. Wildcard parameter "?" doesn't equal java.lang.String,
+ // and e.g. Set doesn't equal TreeSet, so roll our own comparison if
+ // ParameterizedTypeImpl.equals() returns false
+ if (typeToMock.equals(mockType)) {
+ result = true;
+ } else {
+ ParameterizedType genericTypeToMock = (ParameterizedType) typeToMock;
+ ParameterizedType genericMockType = (ParameterizedType) mockType;
+ Type[] actualTypeArguments = genericTypeToMock.getActualTypeArguments();
+ Type[] actualTypeArguments2 = genericMockType.getActualTypeArguments();
+ // Recurse on type parameters, so we properly test whether e.g. Wildcard bounds
+ // have a match
+ result =
+ recurseOnTypeArguments(
+ injectMocksField, actualTypeArguments, actualTypeArguments2);
+ }
+ } else {
+ // mockType is a non-parameterized Class, i.e. a concrete class.
+ // so walk concrete class' type hierarchy
+ Class> concreteMockClass = (Class>) mockType;
+ Stream mockSuperTypes = getSuperTypes(concreteMockClass);
+ result =
+ mockSuperTypes.anyMatch(
+ mockSuperType ->
+ isCompatibleTypes(
+ typeToMock, mockSuperType, injectMocksField));
+ }
+ } else if (typeToMock instanceof WildcardType) {
+ WildcardType wildcardTypeToMock = (WildcardType) typeToMock;
+ Type[] upperBounds = wildcardTypeToMock.getUpperBounds();
+ result =
+ Arrays.stream(upperBounds)
+ .anyMatch(t -> isCompatibleTypes(t, mockType, injectMocksField));
+ } else if (typeToMock instanceof Class && mockType instanceof Class) {
+ result = ((Class>) typeToMock).isAssignableFrom((Class>) mockType);
+ } // no need to check for GenericArrayType, as Mockito cannot mock this anyway
+
+ return result;
+ }
+
+ private Stream getSuperTypes(Class> concreteMockClass) {
+ Stream mockInterfaces = Arrays.stream(concreteMockClass.getGenericInterfaces());
+ Stream mockSuperTypes =
+ Stream.concat(mockInterfaces, Stream.of(concreteMockClass.getGenericSuperclass()));
+ return mockSuperTypes;
+ }
+
+ private boolean recurseOnTypeArguments(
+ Field injectMocksField, Type[] actualTypeArguments, Type[] actualTypeArguments2) {
+ boolean isCompatible = true;
+ for (int i = 0; i < actualTypeArguments.length; i++) {
+ Type actualTypeArgument = actualTypeArguments[i];
+ Type actualTypeArgument2 = actualTypeArguments2[i];
+ if (actualTypeArgument instanceof TypeVariable) {
+ TypeVariable> typeVariable = (TypeVariable>) actualTypeArgument;
+ // this is a TypeVariable declared by the class under test that turned
+ // up in one of its fields,
+ // e.g. class ClassUnderTest { List tList; Set tSet}
+ // The TypeVariable`s actual type is declared by the field containing
+ // the object under test, i.e. the field annotated with @InjectMocks
+ // e.g. @InjectMocks ClassUnderTest underTest = ..
+
+ Type genericType = injectMocksField.getGenericType();
+ if (genericType instanceof ParameterizedType) {
+ Type[] injectMocksFieldTypeParameters =
+ ((ParameterizedType) genericType).getActualTypeArguments();
+ // Find index of given TypeVariable where it was defined, e.g. 0 for T1 in
+ // ClassUnderTest
+ // (we're always able to find it, otherwise test class wouldn't have compiled))
+ TypeVariable>[] genericTypeParameters =
+ injectMocksField.getType().getTypeParameters();
+ int variableIndex = -1;
+ for (int i2 = 0; i2 < genericTypeParameters.length; i2++) {
+ if (genericTypeParameters[i2].equals(typeVariable)) {
+ variableIndex = i2;
+ break;
+ }
+ }
+ // now test whether actual type for the type variable is compatible, e.g. for
+ // class ClassUnderTest {..}
+ // T1 would be the String in
+ // ClassUnderTest underTest = ..
+ isCompatible &=
+ isCompatibleTypes(
+ injectMocksFieldTypeParameters[variableIndex],
+ actualTypeArgument2,
+ injectMocksField);
+ } else {
+ // must be a concrete class, recurse on super types that may have type
+ // parameters
+ isCompatible &=
+ getSuperTypes((Class>) genericType)
+ .anyMatch(
+ superType ->
+ isCompatibleTypes(
+ superType,
+ actualTypeArgument2,
+ injectMocksField));
+ }
+ } else {
+ isCompatible &=
+ isCompatibleTypes(
+ actualTypeArgument, actualTypeArgument2, injectMocksField);
+ }
+ }
+ return isCompatible;
+ }
+
@Override
public OngoingInjector filterCandidate(
final Collection mocks,
final Field candidateFieldToBeInjected,
final List allRemainingCandidateFields,
- final Object injectee) {
+ final Object injectee,
+ final Field injectMocksField) {
List mockTypeMatches = new ArrayList<>();
for (Object mock : mocks) {
if (candidateFieldToBeInjected.getType().isAssignableFrom(mock.getClass())) {
- mockTypeMatches.add(mock);
- }
+ Type mockType = MockUtil.getMockSettings(mock).getGenericTypeToMock();
+ Type typeToMock = candidateFieldToBeInjected.getGenericType();
+ boolean bothHaveTypeInfo = typeToMock != null && mockType != null;
+ if (bothHaveTypeInfo) {
+ // be more specific if generic type information is available
+ if (isCompatibleTypes(typeToMock, mockType, injectMocksField)) {
+ mockTypeMatches.add(mock);
+ } // else filter out mock, as generic types don't match
+ } else {
+ // field is assignable from mock class, but no generic type information
+ // is available (can happen with programmatically created Mocks where no
+ // genericTypeToMock was supplied)
+ mockTypeMatches.add(mock);
+ }
+ } // else filter out mock
+ // BTW mocks may contain Spy objects with their original class (seemingly before
+ // being wrapped), and MockUtil.getMockSettings() throws exception for those
}
- return next.filterCandidate(
- mockTypeMatches, candidateFieldToBeInjected, allRemainingCandidateFields, injectee);
+ boolean wasMultipleMatches = mockTypeMatches.size() > 1;
+
+ OngoingInjector result =
+ next.filterCandidate(
+ mockTypeMatches,
+ candidateFieldToBeInjected,
+ allRemainingCandidateFields,
+ injectee,
+ injectMocksField);
+
+ if (wasMultipleMatches) {
+ // we had found multiple mocks matching by type, see whether following filters
+ // were able to reduce this to single match (e.g. by filtering for matching field names)
+ if (result == OngoingInjector.nop) {
+ // nope, following filters cannot reduce this to a single match
+ throw moreThanOneMockCandidate(candidateFieldToBeInjected, mocks);
+ }
+ }
+ return result;
}
}
diff --git a/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java b/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java
index 592f79a81e..365c350e93 100644
--- a/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java
+++ b/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java
@@ -5,7 +5,11 @@
package org.mockito.internal.configuration.plugins;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
+
+import org.mockito.MockMakers;
import org.mockito.plugins.AnnotationEngine;
import org.mockito.plugins.DoNotMockEnforcer;
import org.mockito.plugins.InstantiatorProvider2;
@@ -16,19 +20,23 @@
import org.mockito.plugins.PluginSwitch;
import org.mockito.plugins.StackTraceCleanerProvider;
-class DefaultMockitoPlugins implements MockitoPlugins {
+public class DefaultMockitoPlugins implements MockitoPlugins {
private static final Map DEFAULT_PLUGINS = new HashMap<>();
- static final String INLINE_ALIAS = "mock-maker-inline";
- static final String PROXY_ALIAS = "mock-maker-proxy";
+ static final String INLINE_ALIAS = MockMakers.INLINE;
+ static final String PROXY_ALIAS = MockMakers.PROXY;
+ static final String SUBCLASS_ALIAS = MockMakers.SUBCLASS;
+ public static final Set MOCK_MAKER_ALIASES = new HashSet<>();
static final String MODULE_ALIAS = "member-accessor-module";
+ static final String REFLECTION_ALIAS = "member-accessor-reflection";
+ public static final Set MEMBER_ACCESSOR_ALIASES = new HashSet<>();
static {
// Keep the mapping: plugin interface name -> plugin implementation class name
DEFAULT_PLUGINS.put(PluginSwitch.class.getName(), DefaultPluginSwitch.class.getName());
DEFAULT_PLUGINS.put(
MockMaker.class.getName(),
- "org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker");
+ "org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker");
DEFAULT_PLUGINS.put(
StackTraceCleanerProvider.class.getName(),
"org.mockito.internal.exceptions.stacktrace.DefaultStackTraceCleanerProvider");
@@ -41,16 +49,27 @@ class DefaultMockitoPlugins implements MockitoPlugins {
DEFAULT_PLUGINS.put(
INLINE_ALIAS, "org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker");
DEFAULT_PLUGINS.put(PROXY_ALIAS, "org.mockito.internal.creation.proxy.ProxyMockMaker");
+ DEFAULT_PLUGINS.put(
+ SUBCLASS_ALIAS, "org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker");
DEFAULT_PLUGINS.put(
MockitoLogger.class.getName(), "org.mockito.internal.util.ConsoleMockitoLogger");
DEFAULT_PLUGINS.put(
MemberAccessor.class.getName(),
- "org.mockito.internal.util.reflection.ReflectionMemberAccessor");
+ "org.mockito.internal.util.reflection.ModuleMemberAccessor");
DEFAULT_PLUGINS.put(
MODULE_ALIAS, "org.mockito.internal.util.reflection.ModuleMemberAccessor");
+ DEFAULT_PLUGINS.put(
+ REFLECTION_ALIAS, "org.mockito.internal.util.reflection.ReflectionMemberAccessor");
DEFAULT_PLUGINS.put(
DoNotMockEnforcer.class.getName(),
"org.mockito.internal.configuration.DefaultDoNotMockEnforcer");
+
+ MOCK_MAKER_ALIASES.add(INLINE_ALIAS);
+ MOCK_MAKER_ALIASES.add(PROXY_ALIAS);
+ MOCK_MAKER_ALIASES.add(SUBCLASS_ALIAS);
+
+ MEMBER_ACCESSOR_ALIASES.add(MODULE_ALIAS);
+ MEMBER_ACCESSOR_ALIASES.add(REFLECTION_ALIAS);
}
@Override
@@ -59,7 +78,7 @@ public T getDefaultPlugin(Class pluginType) {
return create(pluginType, className);
}
- String getDefaultPluginClass(String classOrAlias) {
+ public static String getDefaultPluginClass(String classOrAlias) {
return DEFAULT_PLUGINS.get(classOrAlias);
}
diff --git a/src/main/java/org/mockito/internal/configuration/plugins/PluginInitializer.java b/src/main/java/org/mockito/internal/configuration/plugins/PluginInitializer.java
index a88f0cc19f..b042a84396 100644
--- a/src/main/java/org/mockito/internal/configuration/plugins/PluginInitializer.java
+++ b/src/main/java/org/mockito/internal/configuration/plugins/PluginInitializer.java
@@ -18,12 +18,10 @@ class PluginInitializer {
private final PluginSwitch pluginSwitch;
private final Set alias;
- private final DefaultMockitoPlugins plugins;
- PluginInitializer(PluginSwitch pluginSwitch, Set alias, DefaultMockitoPlugins plugins) {
+ PluginInitializer(PluginSwitch pluginSwitch, Set alias) {
this.pluginSwitch = pluginSwitch;
this.alias = alias;
- this.plugins = plugins;
}
/**
@@ -47,7 +45,7 @@ public T loadImpl(Class service) {
new PluginFinder(pluginSwitch).findPluginClass(Iterables.toIterable(resources));
if (classOrAlias != null) {
if (alias.contains(classOrAlias)) {
- classOrAlias = plugins.getDefaultPluginClass(classOrAlias);
+ classOrAlias = DefaultMockitoPlugins.getDefaultPluginClass(classOrAlias);
}
Class> pluginClass = loader.loadClass(classOrAlias);
Object plugin = pluginClass.getDeclaredConstructor().newInstance();
@@ -79,7 +77,7 @@ public List loadImpls(Class service) {
List impls = new ArrayList<>();
for (String classOrAlias : classesOrAliases) {
if (alias.contains(classOrAlias)) {
- classOrAlias = plugins.getDefaultPluginClass(classOrAlias);
+ classOrAlias = DefaultMockitoPlugins.getDefaultPluginClass(classOrAlias);
}
Class> pluginClass = loader.loadClass(classOrAlias);
Object plugin = pluginClass.getDeclaredConstructor().newInstance();
diff --git a/src/main/java/org/mockito/internal/configuration/plugins/PluginLoader.java b/src/main/java/org/mockito/internal/configuration/plugins/PluginLoader.java
index 3f33dffdc6..e554173326 100644
--- a/src/main/java/org/mockito/internal/configuration/plugins/PluginLoader.java
+++ b/src/main/java/org/mockito/internal/configuration/plugins/PluginLoader.java
@@ -27,8 +27,7 @@ class PluginLoader {
PluginLoader(PluginSwitch pluginSwitch) {
this(
new DefaultMockitoPlugins(),
- new PluginInitializer(
- pluginSwitch, Collections.emptySet(), new DefaultMockitoPlugins()));
+ new PluginInitializer(pluginSwitch, Collections.emptySet()));
}
/**
@@ -40,10 +39,7 @@ class PluginLoader {
PluginLoader(PluginSwitch pluginSwitch, String... alias) {
this(
new DefaultMockitoPlugins(),
- new PluginInitializer(
- pluginSwitch,
- new HashSet<>(Arrays.asList(alias)),
- new DefaultMockitoPlugins()));
+ new PluginInitializer(pluginSwitch, new HashSet<>(Arrays.asList(alias))));
}
/**
diff --git a/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java b/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java
index 01e89d2014..72f5d8e7d5 100644
--- a/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java
+++ b/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java
@@ -23,12 +23,13 @@ class PluginRegistry {
private final MockMaker mockMaker =
new PluginLoader(
pluginSwitch,
- DefaultMockitoPlugins.INLINE_ALIAS,
- DefaultMockitoPlugins.PROXY_ALIAS)
+ DefaultMockitoPlugins.MOCK_MAKER_ALIASES.toArray(new String[0]))
.loadPlugin(MockMaker.class);
private final MemberAccessor memberAccessor =
- new PluginLoader(pluginSwitch, DefaultMockitoPlugins.MODULE_ALIAS)
+ new PluginLoader(
+ pluginSwitch,
+ DefaultMockitoPlugins.MEMBER_ACCESSOR_ALIASES.toArray(new String[0]))
.loadPlugin(MemberAccessor.class);
private final StackTraceCleanerProvider stackTraceCleanerProvider =
diff --git a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java
index f73a718298..7bef7764d4 100644
--- a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java
+++ b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java
@@ -16,6 +16,7 @@
import static org.mockito.internal.util.collections.Sets.newSet;
import java.io.Serializable;
+import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -254,11 +255,23 @@ public MockSettings strictness(Strictness strictness) {
return this;
}
+ @Override
+ public MockSettings mockMaker(String mockMaker) {
+ this.mockMaker = mockMaker;
+ return this;
+ }
+
+ @Override
+ public MockSettings genericTypeToMock(Type genericType) {
+ this.genericTypeToMock = genericType;
+ return this;
+ }
+
private static CreationSettings validatedSettings(
Class typeToMock, CreationSettings source) {
MockCreationValidator validator = new MockCreationValidator();
- validator.validateType(typeToMock);
+ validator.validateType(typeToMock, source.getMockMaker());
validator.validateExtraInterfaces(typeToMock, source.getExtraInterfaces());
validator.validateMockedType(typeToMock, source.getSpiedInstance());
diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java
index 72ab81adbd..009daa4ccb 100644
--- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java
+++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java
@@ -94,8 +94,8 @@ public InlineBytecodeGenerator(
.with(Implementation.Context.Disabled.Factory.INSTANCE)
.with(MethodGraph.Compiler.ForDeclaredMethods.INSTANCE)
.ignore(isSynthetic().and(not(isConstructor())).or(isDefaultFinalizer()));
- mocked = new WeakConcurrentSet<>(WeakConcurrentSet.Cleaner.INLINE);
- flatMocked = new WeakConcurrentSet<>(WeakConcurrentSet.Cleaner.INLINE);
+ mocked = new WeakConcurrentSet<>(WeakConcurrentSet.Cleaner.MANUAL);
+ flatMocked = new WeakConcurrentSet<>(WeakConcurrentSet.Cleaner.MANUAL);
String identifier = RandomString.make();
subclassEngine =
new TypeCachingBytecodeGenerator(
@@ -205,6 +205,7 @@ public Class extends T> mockClass(MockFeatures features) {
boolean subclassingRequired =
!features.interfaces.isEmpty()
|| features.serializableMode != SerializableMode.NONE
+ || features.stripAnnotations
|| Modifier.isAbstract(features.mockedType.getModifiers());
checkSupportedCombination(subclassingRequired, features);
@@ -299,6 +300,9 @@ private void triggerRetransformation(Set> types, boolean flat) {
lastException = null;
}
}
+
+ mocked.expungeStaleEntries();
+ flatMocked.expungeStaleEntries();
}
private void assureCanReadMockito(Set> types) {
@@ -413,6 +417,7 @@ public synchronized void clearAllCaches() {
}
mocked.clear();
flatMocked.clear();
+ subclassEngine.clearAllCaches();
try {
instrumentation.retransformClasses(types.toArray(new Class>[0]));
} catch (UnmodifiableClassException e) {
diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java
index 1a19a92732..d40967f94e 100644
--- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java
+++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java
@@ -192,13 +192,13 @@ class InlineDelegateByteBuddyMockMaker
private final BytecodeGenerator bytecodeGenerator;
private final WeakConcurrentMap mocks =
- new WeakConcurrentMap.WithInlinedExpunction();
+ new WeakConcurrentMap<>(false);
private final DetachedThreadLocal, MockMethodInterceptor>> mockedStatics =
- new DetachedThreadLocal<>(DetachedThreadLocal.Cleaner.INLINE);
+ new DetachedThreadLocal<>(DetachedThreadLocal.Cleaner.MANUAL);
private final DetachedThreadLocal, BiConsumer>>
- mockedConstruction = new DetachedThreadLocal<>(DetachedThreadLocal.Cleaner.INLINE);
+ mockedConstruction = new DetachedThreadLocal<>(DetachedThreadLocal.Cleaner.MANUAL);
private final ThreadLocal mockitoConstruction = ThreadLocal.withInitial(() -> false);
@@ -382,6 +382,7 @@ private T doCreateMock(
if (instance instanceof MockAccess) {
((MockAccess) instance).setMockitoInterceptor(mockMethodInterceptor);
}
+ mocks.expungeStaleEntries();
return instance;
} catch (InstantiationException e) {
throw new MockitoException(
@@ -446,9 +447,7 @@ private RuntimeException prettifyFailure(
"IBM J9 VM",
"Early IBM virtual machine are known to have issues with Mockito, please upgrade to an up-to-date version.\n",
"Hotspot",
- Platform.isJava8BelowUpdate45()
- ? "Java 8 early builds have bugs that were addressed in Java 1.8.0_45, please update your JDK!\n"
- : ""),
+ ""),
Platform.describe(),
"",
"You are seeing this disclaimer because Mockito is configured to create inlined mocks.",
@@ -496,6 +495,7 @@ public void resetMock(Object mock, MockHandler newHandler, MockCreationSettings
if (mock instanceof MockAccess) {
((MockAccess) mock).setMockitoInterceptor(mockMethodInterceptor);
}
+ mocks.expungeStaleEntries();
}
}
@@ -570,6 +570,7 @@ public StaticMockControl createStaticMock(
interceptors = new WeakHashMap<>();
mockedStatics.set(interceptors);
}
+ mockedStatics.getBackingMap().expungeStaleEntries();
return new InlineStaticMockControl<>(type, interceptors, settings, handler);
}
@@ -598,6 +599,7 @@ public ConstructionMockControl createConstructionMock(
interceptors = new WeakHashMap<>();
mockedConstruction.set(interceptors);
}
+ mockedConstruction.getBackingMap().expungeStaleEntries();
return new InlineConstructionMockControl<>(
type, settingsFactory, handlerFactory, mockInitializer, interceptors);
diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java
index fc92e49acf..2c3649ea0e 100644
--- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java
+++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java
@@ -4,8 +4,8 @@
*/
package org.mockito.internal.creation.bytebuddy;
+import static net.bytebuddy.matcher.ElementMatchers.isAccessibleTo;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
-import static net.bytebuddy.matcher.ElementMatchers.isPrivate;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.not;
@@ -51,7 +51,7 @@
import org.mockito.exceptions.base.MockitoException;
import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.internal.creation.bytebuddy.inject.MockMethodDispatcher;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.debugging.LocationFactory;
import org.mockito.internal.exceptions.stacktrace.ConditionalStackTraceFilter;
import org.mockito.internal.invocation.RealMethod;
import org.mockito.internal.invocation.SerializableMethod;
@@ -132,11 +132,7 @@ public Callable> handle(Object instance, Method origin, Object[] arguments) th
}
return new ReturnValueWrapper(
interceptor.doIntercept(
- instance,
- origin,
- arguments,
- realMethod,
- new LocationImpl(new Throwable(), true)));
+ instance, origin, arguments, realMethod, LocationFactory.create(true)));
}
@Override
@@ -154,7 +150,7 @@ public Callable> handleStatic(Class> type, Method origin, Object[] arguments
origin,
arguments,
new StaticMethodCall(selfCallInfo, type, origin, arguments),
- new LocationImpl(new Throwable(), true)));
+ LocationFactory.create(true)));
}
@Override
@@ -172,7 +168,7 @@ public boolean isMock(Object instance) {
@Override
public boolean isMocked(Object instance) {
- return selfCallInfo.checkSelfCall(instance) && isMock(instance);
+ return isMock(instance) && selfCallInfo.checkSelfCall(instance);
}
@Override
@@ -402,7 +398,7 @@ public MethodVisitor wrap(
.getSuperClass()
.asErasure()
.getDeclaredMethods()
- .filter(isConstructor().and(not(isPrivate())));
+ .filter(isConstructor().and(isAccessibleTo(instrumentedType)));
int arguments = Integer.MAX_VALUE;
boolean packagePrivate = true;
MethodDescription.InDefinedShape current = null;
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 83908cace6..406dea39a5 100644
--- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java
+++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java
@@ -22,7 +22,7 @@
import net.bytebuddy.implementation.bind.annotation.StubValue;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.implementation.bind.annotation.This;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.debugging.LocationFactory;
import org.mockito.internal.invocation.RealMethod;
import org.mockito.invocation.Location;
import org.mockito.invocation.MockHandler;
@@ -53,7 +53,7 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo
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, LocationFactory.create());
}
Object doIntercept(
diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java
index e8dcce229a..6bb74322b3 100644
--- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java
+++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java
@@ -122,9 +122,7 @@ private RuntimeException prettifyFailure(
"IBM J9 VM",
"Early IBM virtual machine are known to have issues with Mockito, please upgrade to an up-to-date version.\n",
"Hotspot",
- Platform.isJava8BelowUpdate45()
- ? "Java 8 early builds have bugs that were addressed in Java 1.8.0_45, please update your JDK!\n"
- : ""),
+ ""),
Platform.describe(),
"",
"Underlying exception : " + generationFailed),
diff --git a/src/main/java/org/mockito/internal/creation/instance/ConstructorInstantiator.java b/src/main/java/org/mockito/internal/creation/instance/ConstructorInstantiator.java
index c882704caa..30094c58b8 100644
--- a/src/main/java/org/mockito/internal/creation/instance/ConstructorInstantiator.java
+++ b/src/main/java/org/mockito/internal/creation/instance/ConstructorInstantiator.java
@@ -64,7 +64,8 @@ private T withParams(Class cls, Object... params) {
@SuppressWarnings("unchecked")
private static T invokeConstructor(Constructor> constructor, Object... params)
- throws java.lang.InstantiationException, IllegalAccessException,
+ throws java.lang.InstantiationException,
+ IllegalAccessException,
InvocationTargetException {
MemberAccessor accessor = Plugins.getMemberAccessor();
return (T) accessor.newInstance(constructor, params);
diff --git a/src/main/java/org/mockito/internal/creation/proxy/ProxyMockMaker.java b/src/main/java/org/mockito/internal/creation/proxy/ProxyMockMaker.java
index faa97e849b..88e6886111 100644
--- a/src/main/java/org/mockito/internal/creation/proxy/ProxyMockMaker.java
+++ b/src/main/java/org/mockito/internal/creation/proxy/ProxyMockMaker.java
@@ -5,7 +5,7 @@
package org.mockito.internal.creation.proxy;
import org.mockito.exceptions.base.MockitoException;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.debugging.LocationFactory;
import org.mockito.internal.invocation.RealMethod;
import org.mockito.internal.util.Platform;
import org.mockito.invocation.MockHandler;
@@ -153,7 +153,12 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
return handler.get()
.handle(
createInvocation(
- proxy, method, args, realMethod, settings, new LocationImpl()));
+ proxy,
+ method,
+ args,
+ realMethod,
+ settings,
+ LocationFactory.create()));
}
}
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 13939f6fbd..9114bcb8cf 100644
--- a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java
+++ b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java
@@ -5,6 +5,7 @@
package org.mockito.internal.creation.settings;
import java.io.Serializable;
+import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -25,6 +26,7 @@ public class CreationSettings implements MockCreationSettings, Serializabl
private static final long serialVersionUID = -6789800638070123629L;
protected Class typeToMock;
+ protected Type genericTypeToMock;
protected Set> extraInterfaces = new LinkedHashSet<>();
protected String name;
protected Object spiedInstance;
@@ -46,6 +48,7 @@ public class CreationSettings implements MockCreationSettings, Serializabl
private Object outerClassInstance;
private Object[] constructorArgs;
protected Strictness strictness = null;
+ protected String mockMaker;
public CreationSettings() {}
@@ -53,6 +56,7 @@ public CreationSettings() {}
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.genericTypeToMock = copy.genericTypeToMock;
this.extraInterfaces = copy.extraInterfaces;
this.name = copy.name;
this.spiedInstance = copy.spiedInstance;
@@ -68,6 +72,7 @@ public CreationSettings(CreationSettings copy) {
this.constructorArgs = copy.getConstructorArgs();
this.strictness = copy.strictness;
this.stripAnnotations = copy.stripAnnotations;
+ this.mockMaker = copy.mockMaker;
}
@Override
@@ -80,6 +85,11 @@ public CreationSettings setTypeToMock(Class typeToMock) {
return this;
}
+ public CreationSettings setGenericTypeToMock(Type genericTypeToMock) {
+ this.genericTypeToMock = genericTypeToMock;
+ return this;
+ }
+
@Override
public Set> getExtraInterfaces() {
return extraInterfaces;
@@ -178,4 +188,14 @@ public boolean isLenient() {
public Strictness getStrictness() {
return strictness;
}
+
+ @Override
+ public String getMockMaker() {
+ return mockMaker;
+ }
+
+ @Override
+ public Type getGenericTypeToMock() {
+ return genericTypeToMock;
+ }
}
diff --git a/src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java b/src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java
new file mode 100644
index 0000000000..e8ee387c0a
--- /dev/null
+++ b/src/main/java/org/mockito/internal/debugging/Java8LocationImpl.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.debugging;
+
+import java.io.Serializable;
+
+import org.mockito.internal.exceptions.stacktrace.StackTraceFilter;
+import org.mockito.invocation.Location;
+
+class Java8LocationImpl implements Location, Serializable {
+
+ private static final long serialVersionUID = -9054861157390980624L;
+ // Limit the amount of objects being created, as this class is heavily instantiated:
+ private static final StackTraceFilter stackTraceFilter = new StackTraceFilter();
+
+ private String stackTraceLine;
+ private String sourceFile;
+
+ public Java8LocationImpl(Throwable stackTraceHolder, boolean isInline) {
+ this(stackTraceFilter, stackTraceHolder, isInline);
+ }
+
+ private Java8LocationImpl(
+ StackTraceFilter stackTraceFilter, Throwable stackTraceHolder, boolean isInline) {
+ computeStackTraceInformation(stackTraceFilter, stackTraceHolder, isInline);
+ }
+
+ @Override
+ public String toString() {
+ return stackTraceLine;
+ }
+
+ /**
+ * Eagerly compute the stacktrace line from the stackTraceHolder. Storing the Throwable is
+ * memory-intensive for tests that have large stacktraces and have a lot of invocations on
+ * mocks.
+ */
+ private void computeStackTraceInformation(
+ StackTraceFilter stackTraceFilter, Throwable stackTraceHolder, boolean isInline) {
+ StackTraceElement filtered = stackTraceFilter.filterFirst(stackTraceHolder, isInline);
+
+ // there are corner cases where exception can have a null or empty stack trace
+ // for example, a custom exception can override getStackTrace() method
+ if (filtered == null) {
+ this.stackTraceLine = "-> at <>";
+ this.sourceFile = "";
+ } else {
+ this.stackTraceLine = "-> at " + filtered;
+ this.sourceFile = filtered.getFileName();
+ }
+ }
+
+ @Override
+ public String getSourceFile() {
+ return sourceFile;
+ }
+}
diff --git a/src/main/java/org/mockito/internal/debugging/Localized.java b/src/main/java/org/mockito/internal/debugging/Localized.java
index d1d7912dc4..3abcf29555 100644
--- a/src/main/java/org/mockito/internal/debugging/Localized.java
+++ b/src/main/java/org/mockito/internal/debugging/Localized.java
@@ -13,7 +13,7 @@ public class Localized {
public Localized(T object) {
this.object = object;
- location = new LocationImpl();
+ location = LocationFactory.create();
}
public T getObject() {
diff --git a/src/main/java/org/mockito/internal/debugging/LocationFactory.java b/src/main/java/org/mockito/internal/debugging/LocationFactory.java
new file mode 100644
index 0000000000..3d936ae761
--- /dev/null
+++ b/src/main/java/org/mockito/internal/debugging/LocationFactory.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.debugging;
+
+import org.mockito.invocation.Location;
+
+public final class LocationFactory {
+ private static final Factory factory = createLocationFactory();
+
+ private LocationFactory() {}
+
+ public static Location create() {
+ return create(false);
+ }
+
+ public static Location create(boolean inline) {
+ return factory.create(inline);
+ }
+
+ private interface Factory {
+ Location create(boolean inline);
+ }
+
+ private static Factory createLocationFactory() {
+ try {
+ // On some platforms, like Android, the StackWalker APIs may not be
+ // available, in this case we have to fallback to Java 8 style of stack
+ // traversing.
+ Class.forName("java.lang.StackWalker");
+ return new DefaultLocationFactory();
+ } catch (ClassNotFoundException e) {
+ return new Java8LocationFactory();
+ }
+ }
+
+ private static final class Java8LocationFactory implements Factory {
+ @Override
+ public Location create(boolean inline) {
+ return new Java8LocationImpl(new Throwable(), inline);
+ }
+ }
+
+ private static final class DefaultLocationFactory implements Factory {
+ @Override
+ public Location create(boolean inline) {
+ return new LocationImpl(inline);
+ }
+ }
+}
diff --git a/src/main/java/org/mockito/internal/debugging/LocationImpl.java b/src/main/java/org/mockito/internal/debugging/LocationImpl.java
index cf255013e2..ea72eeb7fd 100644
--- a/src/main/java/org/mockito/internal/debugging/LocationImpl.java
+++ b/src/main/java/org/mockito/internal/debugging/LocationImpl.java
@@ -4,64 +4,208 @@
*/
package org.mockito.internal.debugging;
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.exceptions.stacktrace.StackTraceCleaner;
+import org.mockito.exceptions.stacktrace.StackTraceCleaner.StackFrameMetadata;
+import org.mockito.internal.configuration.plugins.Plugins;
+import org.mockito.internal.exceptions.stacktrace.DefaultStackTraceCleaner;
+import org.mockito.invocation.Location;
+
import java.io.Serializable;
+import java.lang.StackWalker.Option;
+import java.lang.StackWalker.StackFrame;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
-import org.mockito.internal.exceptions.stacktrace.StackTraceFilter;
-import org.mockito.invocation.Location;
+class LocationImpl implements Location, Serializable {
+ private static final long serialVersionUID = 2954388321980069195L;
-public class LocationImpl implements Location, Serializable {
+ private static final String UNEXPECTED_ERROR_SUFFIX =
+ "\nThis is unexpected and is likely due to a change in either Java's StackWalker or Reflection APIs."
+ + "\nIt's worth trying to upgrade to a newer version of Mockito, or otherwise to file a bug report.";
- private static final long serialVersionUID = -9054861157390980624L;
- // Limit the amount of objects being created, as this class is heavily instantiated:
- private static final StackTraceFilter stackTraceFilter = new StackTraceFilter();
+ /**
+ * This is an unfortunate buffer. Inside StackWalker, a buffer is created, which is resized by
+ * doubling. The resizing also allocates a tonne of StackFrame elements. If we traverse more than
+ * BUFFER_SIZE elements, the resulting resize can significantly affect the overall cost of the operation.
+ * If we traverse fewer than this number, we are inefficient. Empirically, 16 is enough stack frames
+ * for a simple stub+call operation to succeed without resizing, as measured on Java 11.
+ */
+ private static final int BUFFER_SIZE = 16;
- private String stackTraceLine;
- private String sourceFile;
+ private static final StackWalker STACK_WALKER = stackWalker();
- public LocationImpl() {
- this(new Throwable(), false);
- }
+ private static final String PREFIX = "-> at ";
- public LocationImpl(Throwable stackTraceHolder, boolean isInline) {
- this(stackTraceFilter, stackTraceHolder, isInline);
- }
+ private static final StackTraceCleaner CLEANER =
+ Plugins.getStackTraceCleanerProvider()
+ .getStackTraceCleaner(new DefaultStackTraceCleaner());
- public LocationImpl(StackTraceFilter stackTraceFilter) {
- this(stackTraceFilter, new Throwable(), false);
+ /**
+ * In Java, allocating lambdas is cheap, but not free. stream.map(this::doSomething)
+ * will allocate a Function object each time the function is called (although not
+ * per element). By assigning these Functions and Predicates to variables, we can
+ * avoid the memory allocation.
+ */
+ private static final Function toStackFrameMetadata =
+ MetadataShim::new;
+
+ private static final Predicate cleanerIsIn = CLEANER::isIn;
+
+ private static final int FRAMES_TO_SKIP = framesToSkip();
+
+ private final StackFrameMetadata sfm;
+ private volatile String stackTraceLine;
+
+ LocationImpl(boolean isInline) {
+ this.sfm = getStackFrame(isInline);
}
- private LocationImpl(
- StackTraceFilter stackTraceFilter, Throwable stackTraceHolder, boolean isInline) {
- computeStackTraceInformation(stackTraceFilter, stackTraceHolder, isInline);
+ @Override
+ public String getSourceFile() {
+ return sfm.getFileName();
}
@Override
public String toString() {
+ return stackTraceLine();
+ }
+
+ private String stackTraceLine() {
+ if (stackTraceLine == null) {
+ synchronized (this) {
+ if (stackTraceLine == null) {
+ stackTraceLine = PREFIX + sfm.toString();
+ }
+ }
+ }
return stackTraceLine;
}
+ private static StackFrameMetadata getStackFrame(boolean isInline) {
+ return stackWalk(
+ stream ->
+ stream.map(toStackFrameMetadata)
+ .skip(FRAMES_TO_SKIP)
+ .filter(cleanerIsIn)
+ .skip(isInline ? 1 : 0)
+ .findFirst()
+ .orElseThrow(
+ () -> new MockitoException(noStackTraceFailureMessage())));
+ }
+
+ private static boolean usingDefaultStackTraceCleaner() {
+ return CLEANER instanceof DefaultStackTraceCleaner;
+ }
+
+ private static String noStackTraceFailureMessage() {
+ if (usingDefaultStackTraceCleaner()) {
+ return "Mockito could not find the first non-Mockito stack frame."
+ + UNEXPECTED_ERROR_SUFFIX;
+ } else {
+ String cleanerType = CLEANER.getClass().getName();
+ String fmt =
+ "Mockito could not find the first non-Mockito stack frame. A custom stack frame cleaner \n"
+ + "(type %s) is in use and this has mostly likely filtered out all the relevant stack frames.";
+ return String.format(fmt, cleanerType);
+ }
+ }
+
/**
- * Eagerly compute the stacktrace line from the stackTraceHolder. Storing the Throwable is
- * memory-intensive for tests that have large stacktraces and have a lot of invocations on
- * mocks.
+ * In order to trigger the stack walker, we create some reflective frames. These need to be skipped so as to
+ * ensure there are no non-Mockito frames at the top of the stack trace.
*/
- private void computeStackTraceInformation(
- StackTraceFilter stackTraceFilter, Throwable stackTraceHolder, boolean isInline) {
- StackTraceElement filtered = stackTraceFilter.filterFirst(stackTraceHolder, isInline);
-
- // there are corner cases where exception can have a null or empty stack trace
- // for example, a custom exception can override getStackTrace() method
- if (filtered == null) {
- this.stackTraceLine = "-> at <>";
- this.sourceFile = "";
- } else {
- this.stackTraceLine = "-> at " + filtered;
- this.sourceFile = filtered.getFileName();
+ private static int framesToSkip() {
+ return stackWalk(
+ stream -> {
+ List metadata =
+ stream.map(toStackFrameMetadata)
+ .map(StackFrameMetadata::getClassName)
+ .collect(Collectors.toList());
+ return metadata.indexOf(LocationImpl.class.getName());
+ });
+ }
+
+ private static T stackWalk(Function, T> function) {
+ return (T) STACK_WALKER.walk(function);
+ }
+
+ private static StackWalker stackWalker() {
+ return StackWalker.getInstance(
+ Collections.singleton(Option.SHOW_REFLECT_FRAMES), BUFFER_SIZE);
+ }
+
+ private static final class MetadataShim implements StackFrameMetadata, Serializable {
+ private static final long serialVersionUID = 8491903719411428648L;
+ private final StackFrame stackFrame;
+
+ private MetadataShim(StackFrame stackFrame) {
+ this.stackFrame = stackFrame;
+ }
+
+ @Override
+ public String getClassName() {
+ return stackFrame.getClassName();
+ }
+
+ @Override
+ public String getMethodName() {
+ return stackFrame.getMethodName();
+ }
+
+ @Override
+ public String getFileName() {
+ return stackFrame.getFileName();
+ }
+
+ @Override
+ public int getLineNumber() {
+ return stackFrame.getLineNumber();
+ }
+
+ @Override
+ public String toString() {
+ return stackFrame.toString();
+ }
+
+ /**
+ * Ensure that this type remains serializable.
+ */
+ private Object writeReplace() {
+ return new SerializableShim(stackFrame.toStackTraceElement());
}
}
- @Override
- public String getSourceFile() {
- return sourceFile;
+ private static final class SerializableShim implements StackFrameMetadata, Serializable {
+ private static final long serialVersionUID = 7908320459080898690L;
+ private final StackTraceElement ste;
+
+ private SerializableShim(StackTraceElement ste) {
+ this.ste = ste;
+ }
+
+ @Override
+ public String getClassName() {
+ return ste.getClassName();
+ }
+
+ @Override
+ public String getMethodName() {
+ return ste.getMethodName();
+ }
+
+ @Override
+ public String getFileName() {
+ return ste.getFileName();
+ }
+
+ @Override
+ public int getLineNumber() {
+ return ste.getLineNumber();
+ }
}
}
diff --git a/src/main/java/org/mockito/internal/debugging/LoggingListener.java b/src/main/java/org/mockito/internal/debugging/LoggingListener.java
index 6e0a645ce1..c5ebee4aa5 100644
--- a/src/main/java/org/mockito/internal/debugging/LoggingListener.java
+++ b/src/main/java/org/mockito/internal/debugging/LoggingListener.java
@@ -82,7 +82,7 @@ public String getStubbingInfo() {
if (!unstubbedCalls.isEmpty()) {
lines.add("[Mockito]");
lines.add(
- "[Mockito] Unstubbed method invocations (perhaps missing stubbing in the test?):");
+ "[Mockito] Un-stubbed method invocations (perhaps missing stubbing in the test?):");
lines.add("[Mockito]");
addOrderedList(lines, unstubbedCalls);
}
diff --git a/src/main/java/org/mockito/internal/exceptions/Reporter.java b/src/main/java/org/mockito/internal/exceptions/Reporter.java
index ea670407e7..0609168465 100644
--- a/src/main/java/org/mockito/internal/exceptions/Reporter.java
+++ b/src/main/java/org/mockito/internal/exceptions/Reporter.java
@@ -10,11 +10,30 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
import org.mockito.exceptions.base.MockitoAssertionError;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.exceptions.base.MockitoInitializationException;
-import org.mockito.exceptions.misusing.*;
+import org.mockito.exceptions.misusing.CannotStubVoidMethodWithReturnValue;
+import org.mockito.exceptions.misusing.CannotVerifyStubOnlyMock;
+import org.mockito.exceptions.misusing.FriendlyReminderException;
+import org.mockito.exceptions.misusing.InjectMocksException;
+import org.mockito.exceptions.misusing.InvalidUseOfMatchersException;
+import org.mockito.exceptions.misusing.MissingMethodInvocationException;
+import org.mockito.exceptions.misusing.NotAMockException;
+import org.mockito.exceptions.misusing.NullInsteadOfMockException;
+import org.mockito.exceptions.misusing.PotentialStubbingProblem;
+import org.mockito.exceptions.misusing.RedundantListenerException;
+import org.mockito.exceptions.misusing.UnfinishedMockingSessionException;
+import org.mockito.exceptions.misusing.UnfinishedStubbingException;
+import org.mockito.exceptions.misusing.UnfinishedVerificationException;
+import org.mockito.exceptions.misusing.UnnecessaryStubbingException;
+import org.mockito.exceptions.misusing.WrongTypeOfReturnValue;
import org.mockito.exceptions.verification.MoreThanAllowedActualInvocations;
import org.mockito.exceptions.verification.NeverWantedButInvoked;
import org.mockito.exceptions.verification.NoInteractionsWanted;
@@ -23,16 +42,19 @@
import org.mockito.exceptions.verification.TooManyActualInvocations;
import org.mockito.exceptions.verification.VerificationInOrderFailure;
import org.mockito.exceptions.verification.WantedButNotInvoked;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.debugging.LocationFactory;
import org.mockito.internal.exceptions.util.ScenarioPrinter;
import org.mockito.internal.junit.ExceptionFactory;
import org.mockito.internal.matchers.LocalizedMatcher;
import org.mockito.internal.util.MockUtil;
+import org.mockito.internal.verification.argumentmatching.ArgumentMatchingTool;
import org.mockito.invocation.DescribedInvocation;
import org.mockito.invocation.Invocation;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.invocation.Location;
+import org.mockito.invocation.MatchableInvocation;
import org.mockito.listeners.InvocationListener;
+import org.mockito.mock.MockName;
import org.mockito.mock.SerializableMode;
/**
@@ -84,7 +106,7 @@ public static MockitoException incorrectUseOfApi() {
return new MockitoException(
join(
"Incorrect use of API detected here:",
- new LocationImpl(),
+ LocationFactory.create(),
"",
"You probably stored a reference to OngoingStubbing returned by when() and called stubbing methods like thenReturn() on this reference more than once.",
"Examples of correct usage:",
@@ -262,7 +284,7 @@ public static MockitoException incorrectUseOfAdditionalMatchers(
"Invalid use of argument matchers inside additional matcher "
+ additionalMatcherName
+ " !",
- new LocationImpl(),
+ LocationFactory.create(),
"",
expectedSubMatchersCount
+ " sub matchers expected, "
@@ -295,7 +317,7 @@ public static MockitoException reportNoSubMatchersFound(String additionalMatcher
return new InvalidUseOfMatchersException(
join(
"No matchers found for additional matcher " + additionalMatcherName,
- new LocationImpl(),
+ LocationFactory.create(),
""));
}
@@ -308,7 +330,11 @@ private static Object locationsOf(Collection matchers) {
}
public static AssertionError argumentsAreDifferent(
- String wanted, List actualCalls, List actualLocations) {
+ Invocation actualInvocation,
+ MatchableInvocation matchableInvocation,
+ String wanted,
+ List actualCalls,
+ List actualLocations) {
if (actualCalls == null
|| actualLocations == null
|| actualCalls.size() != actualLocations.size()) {
@@ -322,9 +348,13 @@ public static AssertionError argumentsAreDifferent(
.append("Argument(s) are different! Wanted:\n")
.append(wanted)
.append("\n")
- .append(new LocationImpl())
+ .append(LocationFactory.create())
.append("\n")
- .append("Actual invocations have different arguments:\n");
+ .append("Actual invocations have different arguments");
+
+ appendNotMatchingPositions(actualInvocation, matchableInvocation, messageBuilder);
+
+ messageBuilder.append(":\n");
for (int i = 0; i < actualCalls.size(); i++) {
actualBuilder.append(actualCalls.get(i)).append("\n");
@@ -340,6 +370,30 @@ public static AssertionError argumentsAreDifferent(
messageBuilder.toString(), wanted, actualBuilder.toString());
}
+ /*
+ * Will append the non matching positions only if there are more than 1 arguments to the method.
+ */
+ private static void appendNotMatchingPositions(
+ Invocation actualInvocation,
+ MatchableInvocation matchableInvocation,
+ StringBuilder messageBuilder) {
+ Object[] args = actualInvocation.getArguments();
+ if (args.length <= 1) {
+ return;
+ }
+
+ List indexes =
+ ArgumentMatchingTool.getNotMatchingArgsIndexes(
+ matchableInvocation.getMatchers(), actualInvocation.getArguments());
+
+ if (!indexes.isEmpty()) {
+ messageBuilder
+ .append(" at ")
+ .append(indexes.size() == 1 ? "position " : "positions ")
+ .append(indexes);
+ }
+ }
+
public static MockitoAssertionError wantedButNotInvoked(DescribedInvocation wanted) {
return new WantedButNotInvoked(createWantedButNotInvokedMessage(wanted));
}
@@ -365,7 +419,7 @@ public static MockitoAssertionError wantedButNotInvoked(
}
private static String createWantedButNotInvokedMessage(DescribedInvocation wanted) {
- return join("Wanted but not invoked:", wanted.toString(), new LocationImpl(), "");
+ return join("Wanted but not invoked:", wanted.toString(), LocationFactory.create(), "");
}
public static MockitoAssertionError wantedButNotInvokedInOrder(
@@ -375,7 +429,7 @@ public static MockitoAssertionError wantedButNotInvokedInOrder(
"Verification in order failure",
"Wanted but not invoked:",
wanted.toString(),
- new LocationImpl(),
+ LocationFactory.create(),
"Wanted anywhere AFTER following interaction:",
previous.toString(),
previous.getLocation(),
@@ -400,7 +454,7 @@ private static String createTooManyInvocationsMessage(
return join(
wanted.toString(),
"Wanted " + pluralize(wantedCount) + ":",
- new LocationImpl(),
+ LocationFactory.create(),
"But was " + pluralize(actualCount) + ":",
createAllLocationsMessage(invocations),
"");
@@ -413,7 +467,7 @@ public static MockitoAssertionError neverWantedButInvoked(
join(
wanted.toString(),
"Never wanted here:",
- new LocationImpl(),
+ LocationFactory.create(),
"But invoked here:",
createAllLocationsArgsMessage(invocations)));
}
@@ -463,7 +517,7 @@ private static String createTooFewInvocationsMessage(
"Wanted "
+ discrepancy.getPluralizedWantedCount()
+ (discrepancy.getWantedCount() == 0 ? "." : ":"),
- new LocationImpl(),
+ LocationFactory.create(),
"But was "
+ discrepancy.getPluralizedActualCount()
+ (discrepancy.getActualCount() == 0 ? "." : ":"),
@@ -496,7 +550,7 @@ public static MockitoAssertionError noMoreInteractionsWanted(
return new NoInteractionsWanted(
join(
"No interactions wanted here:",
- new LocationImpl(),
+ LocationFactory.create(),
"But found this interaction on mock '"
+ MockUtil.getMockName(undesired.getMock())
+ "':",
@@ -508,7 +562,7 @@ public static MockitoAssertionError noMoreInteractionsWantedInOrder(Invocation u
return new VerificationInOrderFailure(
join(
"No interactions wanted here:",
- new LocationImpl(),
+ LocationFactory.create(),
"But found this interaction on mock '"
+ MockUtil.getMockName(undesired.getMock())
+ "':",
@@ -527,7 +581,7 @@ public static MockitoAssertionError noInteractionsWanted(
return new NoInteractionsWanted(
join(
"No interactions wanted here:",
- new LocationImpl(),
+ LocationFactory.create(),
"But found these interactions on mock '"
+ MockUtil.getMockName(mock)
+ "':",
@@ -645,7 +699,7 @@ public static MockitoException smartNullPointerException(String invocation, Loca
return new SmartNullPointerException(
join(
"You have a NullPointerException here:",
- new LocationImpl(),
+ LocationFactory.create(),
"because this method call was *not* stubbed correctly:",
location,
invocation,
@@ -841,6 +895,29 @@ public static MockitoException cannotInjectDependency(
details);
}
+ public static MockitoException moreThanOneMockCandidate(
+ Field field, Collection> mockCandidates) {
+ List mockNames =
+ mockCandidates.stream()
+ .map(MockUtil::getMockName)
+ .map(MockName::toString)
+ .collect(Collectors.toList());
+ return new MockitoException(
+ join(
+ "Mockito couldn't inject mock dependency on field "
+ + "'"
+ + field
+ + "' that is annotated with @InjectMocks in your test, ",
+ "because there were multiple matching mocks (i.e. "
+ + "fields annotated with @Mock and having matching type): "
+ + String.join(", ", mockNames)
+ + ".",
+ "If you have multiple fields of same type in your class under test "
+ + "then consider naming the @Mock fields "
+ + "identically to the respective class under test's fields, "
+ + "so Mockito can match them by name."));
+ }
+
private static String exceptionCauseMessageIfAvailable(Exception details) {
if (details.getCause() == null) {
return details.getMessage();
diff --git a/src/main/java/org/mockito/internal/exceptions/stacktrace/DefaultStackTraceCleaner.java b/src/main/java/org/mockito/internal/exceptions/stacktrace/DefaultStackTraceCleaner.java
index 6b0575273e..797515dea1 100644
--- a/src/main/java/org/mockito/internal/exceptions/stacktrace/DefaultStackTraceCleaner.java
+++ b/src/main/java/org/mockito/internal/exceptions/stacktrace/DefaultStackTraceCleaner.java
@@ -13,15 +13,31 @@ public class DefaultStackTraceCleaner implements StackTraceCleaner {
@Override
public boolean isIn(StackTraceElement e) {
- if (isFromMockitoRunner(e.getClassName()) || isFromMockitoRule(e.getClassName())) {
+ return isIn(e.getClassName());
+ }
+
+ @Override
+ public boolean isIn(StackFrameMetadata e) {
+ return isIn(e.getClassName());
+ }
+
+ private boolean isIn(String className) {
+ if (isFromMockitoRunner(className) || isFromMockitoRule(className)) {
return true;
- } else if (isMockDispatcher(e.getClassName()) || isFromMockito(e.getClassName())) {
+ } else if (isMockDispatcher(className)
+ || isFromMockito(className)
+ || isMethodHandle(className)) {
return false;
} else {
return true;
}
}
+ /* Some mock makers (like inline) use java.lang.invoke.MethodHandle to dispatch calls */
+ private static boolean isMethodHandle(String className) {
+ return className.startsWith("java.lang.invoke.MethodHandle");
+ }
+
private static boolean isMockDispatcher(String className) {
return (className.contains("$$EnhancerByMockitoWithCGLIB$$")
|| className.contains("$MockitoMock$"));
diff --git a/src/main/java/org/mockito/internal/hamcrest/HamcrestArgumentMatcher.java b/src/main/java/org/mockito/internal/hamcrest/HamcrestArgumentMatcher.java
index 99869faf73..f9ef55d528 100644
--- a/src/main/java/org/mockito/internal/hamcrest/HamcrestArgumentMatcher.java
+++ b/src/main/java/org/mockito/internal/hamcrest/HamcrestArgumentMatcher.java
@@ -7,14 +7,25 @@
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.mockito.ArgumentMatcher;
-import org.mockito.internal.matchers.VarargMatcher;
+
+import static java.util.Objects.requireNonNull;
public class HamcrestArgumentMatcher implements ArgumentMatcher {
- private final Matcher matcher;
+ private final Matcher matcher;
+ private final Class> type;
public HamcrestArgumentMatcher(Matcher matcher) {
- this.matcher = matcher;
+ this(Void.class, matcher);
+ }
+
+ public HamcrestArgumentMatcher(Matcher matcher, Class type) {
+ this(type, matcher);
+ }
+
+ private HamcrestArgumentMatcher(Class> type, Matcher matcher) {
+ this.type = requireNonNull(type, "type");
+ this.matcher = requireNonNull(matcher, "matcher");
}
@Override
@@ -22,13 +33,14 @@ public boolean matches(Object argument) {
return this.matcher.matches(argument);
}
- public boolean isVarargMatcher() {
- return matcher instanceof VarargMatcher;
- }
-
@Override
public String toString() {
// TODO SF add unit tests and integ test coverage for toString()
return StringDescription.toString(matcher);
}
+
+ @Override
+ public Class> type() {
+ return type;
+ }
}
diff --git a/src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java b/src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java
index 81f801518f..4921f4006d 100644
--- a/src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java
+++ b/src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java
@@ -8,7 +8,7 @@
import java.util.concurrent.Callable;
import org.mockito.internal.creation.DelegatingMethod;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.debugging.LocationFactory;
import org.mockito.internal.invocation.mockref.MockWeakReference;
import org.mockito.internal.progress.SequenceNumber;
import org.mockito.invocation.Invocation;
@@ -71,7 +71,7 @@ private static InterceptedInvocation createInvocation(
RealMethod realMethod,
MockCreationSettings settings) {
return createInvocation(
- mock, invokedMethod, arguments, realMethod, settings, new LocationImpl());
+ mock, invokedMethod, arguments, realMethod, settings, LocationFactory.create());
}
private static MockitoMethod createMockitoMethod(Method method, MockCreationSettings settings) {
diff --git a/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java b/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java
index acc7382e2c..8b50da635d 100644
--- a/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java
+++ b/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java
@@ -4,38 +4,21 @@
*/
package org.mockito.internal.invocation;
-import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS;
-import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.MATCH_EACH_VARARGS_WITH_LAST_MATCHER;
-import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ONE_MATCHER_PER_ARGUMENT;
-
-import java.util.ArrayList;
import java.util.List;
import org.mockito.ArgumentMatcher;
-import org.mockito.internal.hamcrest.HamcrestArgumentMatcher;
import org.mockito.internal.matchers.CapturingMatcher;
-import org.mockito.internal.matchers.VarargMatcher;
import org.mockito.invocation.Invocation;
public class MatcherApplicationStrategy {
private final Invocation invocation;
- private final List> matchers;
- private final MatcherApplicationType matchingType;
+ private final List extends ArgumentMatcher>> matchers;
private MatcherApplicationStrategy(
- Invocation invocation,
- List> matchers,
- MatcherApplicationType matchingType) {
+ Invocation invocation, List extends ArgumentMatcher>> matchers) {
this.invocation = invocation;
- if (matchingType == MATCH_EACH_VARARGS_WITH_LAST_MATCHER) {
- int times = varargLength(invocation);
- this.matchers = appendLastMatcherNTimes(matchers, times);
- } else {
- this.matchers = matchers;
- }
-
- this.matchingType = matchingType;
+ this.matchers = matchers;
}
/**
@@ -51,10 +34,8 @@ private MatcherApplicationStrategy(
* @return never null
*/
public static MatcherApplicationStrategy getMatcherApplicationStrategyFor(
- Invocation invocation, List> matchers) {
-
- MatcherApplicationType type = getMatcherApplicationType(invocation, matchers);
- return new MatcherApplicationStrategy(invocation, matchers, type);
+ Invocation invocation, List extends ArgumentMatcher>> matchers) {
+ return new MatcherApplicationStrategy(invocation, matchers);
}
/**
@@ -74,11 +55,29 @@ public static MatcherApplicationStrategy getMatcherApplicationStrategyFor(
*
*/
public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) {
- if (matchingType == ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS) {
- return false;
+ final boolean maybeVararg =
+ invocation.getMethod().isVarArgs()
+ && invocation.getRawArguments().length == matchers.size();
+
+ if (maybeVararg) {
+ final Class> matcherType = lastMatcherType();
+ final Class> paramType = lastParameterType();
+ if (paramType.isAssignableFrom(matcherType)) {
+ return argsMatch(invocation.getRawArguments(), matchers, action);
+ }
+ }
+
+ if (invocation.getArguments().length == matchers.size()) {
+ return argsMatch(invocation.getArguments(), matchers, action);
}
- Object[] arguments = invocation.getArguments();
+ return false;
+ }
+
+ private boolean argsMatch(
+ Object[] arguments,
+ List extends ArgumentMatcher>> matchers,
+ ArgumentMatcherAction action) {
for (int i = 0; i < arguments.length; i++) {
ArgumentMatcher> matcher = matchers.get(i);
Object argument = arguments[i];
@@ -90,55 +89,12 @@ public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) {
return true;
}
- private static MatcherApplicationType getMatcherApplicationType(
- Invocation invocation, List> matchers) {
- final int rawArguments = invocation.getRawArguments().length;
- final int expandedArguments = invocation.getArguments().length;
- final int matcherCount = matchers.size();
-
- if (expandedArguments == matcherCount) {
- return ONE_MATCHER_PER_ARGUMENT;
- }
-
- if (rawArguments == matcherCount && isLastMatcherVarargMatcher(matchers)) {
- return MATCH_EACH_VARARGS_WITH_LAST_MATCHER;
- }
-
- return ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS;
- }
-
- private static boolean isLastMatcherVarargMatcher(final List> matchers) {
- ArgumentMatcher> argumentMatcher = lastMatcher(matchers);
- if (argumentMatcher instanceof HamcrestArgumentMatcher>) {
- return ((HamcrestArgumentMatcher>) argumentMatcher).isVarargMatcher();
- }
- return argumentMatcher instanceof VarargMatcher;
- }
-
- private static List> appendLastMatcherNTimes(
- List> matchers, int timesToAppendLastMatcher) {
- ArgumentMatcher> lastMatcher = lastMatcher(matchers);
-
- List> expandedMatchers = new ArrayList>(matchers);
- for (int i = 0; i < timesToAppendLastMatcher; i++) {
- expandedMatchers.add(lastMatcher);
- }
- return expandedMatchers;
- }
-
- private static int varargLength(Invocation invocation) {
- int rawArgumentCount = invocation.getRawArguments().length;
- int expandedArgumentCount = invocation.getArguments().length;
- return expandedArgumentCount - rawArgumentCount;
- }
-
- private static ArgumentMatcher> lastMatcher(List> matchers) {
- return matchers.get(matchers.size() - 1);
+ private Class> lastMatcherType() {
+ return matchers.get(matchers.size() - 1).type();
}
- enum MatcherApplicationType {
- ONE_MATCHER_PER_ARGUMENT,
- MATCH_EACH_VARARGS_WITH_LAST_MATCHER,
- ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS;
+ private Class> lastParameterType() {
+ final Class>[] parameterTypes = invocation.getMethod().getParameterTypes();
+ return parameterTypes[parameterTypes.length - 1];
}
}
diff --git a/src/main/java/org/mockito/internal/invocation/TypeSafeMatching.java b/src/main/java/org/mockito/internal/invocation/TypeSafeMatching.java
index b4ca07aca6..8f8af6dbdb 100644
--- a/src/main/java/org/mockito/internal/invocation/TypeSafeMatching.java
+++ b/src/main/java/org/mockito/internal/invocation/TypeSafeMatching.java
@@ -4,15 +4,26 @@
*/
package org.mockito.internal.invocation;
-import java.lang.reflect.Method;
-
import org.mockito.ArgumentMatcher;
+import java.lang.reflect.Method;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
@SuppressWarnings({"unchecked", "rawtypes"})
public class TypeSafeMatching implements ArgumentMatcherAction {
private static final ArgumentMatcherAction TYPE_SAFE_MATCHING_ACTION = new TypeSafeMatching();
+ /**
+ * This cache is in theory unbounded. However, its max size is bounded by the number of types of argument matchers
+ * that are both in the system and being used, which is expected to bound the cache's size to a low number
+ * (<200) in all but the most contrived cases, and form a small percentage of the overall memory usage of those
+ * classes.
+ */
+ private static final ConcurrentMap, Class>> argumentTypeCache =
+ new ConcurrentHashMap<>();
+
private TypeSafeMatching() {}
public static ArgumentMatcherAction matchesTypeSafe() {
@@ -39,11 +50,23 @@ private static boolean isCompatible(ArgumentMatcher> argumentMatcher, Object a
return expectedArgumentType.isInstance(argument);
}
+ private static Class> getArgumentType(ArgumentMatcher> matcher) {
+ Class> argumentMatcherType = matcher.getClass();
+ Class> cached = argumentTypeCache.get(argumentMatcherType);
+ // Avoids a lambda allocation on invocations >=2 for worse perf on invocation 1.
+ if (cached != null) {
+ return cached;
+ } else {
+ return argumentTypeCache.computeIfAbsent(
+ argumentMatcherType, unusedKey -> getArgumentTypeUncached(matcher));
+ }
+ }
+
/**
* Returns the type of {@link ArgumentMatcher#matches(Object)} of the given
* {@link ArgumentMatcher} implementation.
*/
- private static Class> getArgumentType(ArgumentMatcher> argumentMatcher) {
+ private static Class> getArgumentTypeUncached(ArgumentMatcher> argumentMatcher) {
Method[] methods = argumentMatcher.getClass().getMethods();
for (Method method : methods) {
diff --git a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java
index c71386346e..48ba775100 100644
--- a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java
+++ b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java
@@ -10,6 +10,7 @@
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import org.mockito.internal.exceptions.Reporter;
import org.mockito.internal.stubbing.UnusedStubbingReporting;
@@ -68,17 +69,15 @@ private static List potentialArgMismatches(
List matchingStubbings = new LinkedList<>();
for (Stubbing s : stubbings) {
if (UnusedStubbingReporting.shouldBeReported(s)
- && s.getInvocation()
- .getMethod()
- .getName()
- .equals(invocation.getMethod().getName())
+ && Objects.equals(
+ s.getInvocation().getMethod().getName(),
+ invocation.getMethod().getName())
// If stubbing and invocation are in the same source file we assume they are in
// the test code,
// and we don't flag it as mismatch:
- && !s.getInvocation()
- .getLocation()
- .getSourceFile()
- .equals(invocation.getLocation().getSourceFile())) {
+ && !Objects.equals(
+ s.getInvocation().getLocation().getSourceFile(),
+ invocation.getLocation().getSourceFile())) {
matchingStubbings.add(s.getInvocation());
}
}
diff --git a/src/main/java/org/mockito/internal/matchers/And.java b/src/main/java/org/mockito/internal/matchers/And.java
index 417e88dbbe..cffed323f1 100644
--- a/src/main/java/org/mockito/internal/matchers/And.java
+++ b/src/main/java/org/mockito/internal/matchers/And.java
@@ -23,6 +23,13 @@ public boolean matches(Object actual) {
return m1.matches(actual) && m2.matches(actual);
}
+ @Override
+ public Class> type() {
+ return m1.type().isAssignableFrom(m2.type())
+ ? m1.type()
+ : m2.type().isAssignableFrom(m1.type()) ? m2.type() : ArgumentMatcher.super.type();
+ }
+
@Override
public String toString() {
return "and(" + m1 + ", " + m2 + ")";
diff --git a/src/main/java/org/mockito/internal/matchers/Any.java b/src/main/java/org/mockito/internal/matchers/Any.java
index 7ad113feed..2074f81abc 100644
--- a/src/main/java/org/mockito/internal/matchers/Any.java
+++ b/src/main/java/org/mockito/internal/matchers/Any.java
@@ -8,7 +8,7 @@
import org.mockito.ArgumentMatcher;
-public class Any implements ArgumentMatcher, VarargMatcher, Serializable {
+public class Any implements ArgumentMatcher, Serializable {
public static final Any ANY = new Any();
@@ -21,4 +21,9 @@ public boolean matches(Object actual) {
public String toString() {
return "";
}
+
+ @Override
+ public Class> type() {
+ return Object.class;
+ }
}
diff --git a/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java b/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java
index 5138839ddb..7ce9133651 100644
--- a/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java
+++ b/src/main/java/org/mockito/internal/matchers/CapturingMatcher.java
@@ -9,30 +9,43 @@
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.mockito.ArgumentMatcher;
+import org.mockito.internal.util.Primitives;
-@SuppressWarnings("unchecked")
-public class CapturingMatcher
- implements ArgumentMatcher, CapturesArguments, VarargMatcher, Serializable {
+public class CapturingMatcher implements ArgumentMatcher, CapturesArguments, Serializable {
- private final List arguments = new ArrayList<>();
+ private final Class extends T> clazz;
+ private final List arguments = new ArrayList<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
+ public CapturingMatcher(final Class extends T> clazz) {
+ this.clazz = Objects.requireNonNull(clazz);
+ }
+
@Override
public boolean matches(Object argument) {
- return true;
+ if (argument == null) {
+ return true;
+ }
+
+ if (Primitives.isPrimitiveOrWrapper(clazz)) {
+ return Primitives.isAssignableFromWrapper(clazz, argument.getClass());
+ }
+
+ return clazz.isAssignableFrom(argument.getClass());
}
@Override
public String toString() {
- return "";
+ return "";
}
public T getLastValue() {
@@ -42,7 +55,7 @@ public T getLastValue() {
throw noArgumentValueWasCaptured();
}
- return (T) arguments.get(arguments.size() - 1);
+ return arguments.get(arguments.size() - 1);
} finally {
readLock.unlock();
}
@@ -51,19 +64,25 @@ public T getLastValue() {
public List getAllValues() {
readLock.lock();
try {
- return new ArrayList((List) arguments);
+ return new ArrayList<>(arguments);
} finally {
readLock.unlock();
}
}
+ @SuppressWarnings("unchecked")
@Override
public void captureFrom(Object argument) {
writeLock.lock();
try {
- this.arguments.add(argument);
+ this.arguments.add((T) argument);
} finally {
writeLock.unlock();
}
}
+
+ @Override
+ public Class> type() {
+ return clazz;
+ }
}
diff --git a/src/main/java/org/mockito/internal/matchers/Equals.java b/src/main/java/org/mockito/internal/matchers/Equals.java
index 8b07b2da75..bbfad627c4 100644
--- a/src/main/java/org/mockito/internal/matchers/Equals.java
+++ b/src/main/java/org/mockito/internal/matchers/Equals.java
@@ -22,6 +22,11 @@ public boolean matches(Object actual) {
return Equality.areEqual(this.wanted, actual);
}
+ @Override
+ public Class> type() {
+ return wanted != null ? wanted.getClass() : ArgumentMatcher.super.type();
+ }
+
@Override
public String toString() {
return describe(wanted);
diff --git a/src/main/java/org/mockito/internal/matchers/InstanceOf.java b/src/main/java/org/mockito/internal/matchers/InstanceOf.java
index 1b4b30e875..a53b1bda0f 100644
--- a/src/main/java/org/mockito/internal/matchers/InstanceOf.java
+++ b/src/main/java/org/mockito/internal/matchers/InstanceOf.java
@@ -11,7 +11,7 @@
public class InstanceOf implements ArgumentMatcher, Serializable {
- private final Class> clazz;
+ final Class> clazz;
private final String description;
public InstanceOf(Class> clazz) {
@@ -31,18 +31,12 @@ public boolean matches(Object actual) {
}
@Override
- public String toString() {
- return description;
+ public Class> type() {
+ return clazz;
}
- public static class VarArgAware extends InstanceOf implements VarargMatcher {
-
- public VarArgAware(Class> clazz) {
- super(clazz);
- }
-
- public VarArgAware(Class> clazz, String describedAs) {
- super(clazz, describedAs);
- }
+ @Override
+ public String toString() {
+ return description;
}
}
diff --git a/src/main/java/org/mockito/internal/matchers/LocalizedMatcher.java b/src/main/java/org/mockito/internal/matchers/LocalizedMatcher.java
index 00b47de7a9..a54eb6d79e 100644
--- a/src/main/java/org/mockito/internal/matchers/LocalizedMatcher.java
+++ b/src/main/java/org/mockito/internal/matchers/LocalizedMatcher.java
@@ -5,7 +5,7 @@
package org.mockito.internal.matchers;
import org.mockito.ArgumentMatcher;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.debugging.LocationFactory;
import org.mockito.invocation.Location;
@SuppressWarnings("unchecked")
@@ -16,7 +16,7 @@ public class LocalizedMatcher {
public LocalizedMatcher(ArgumentMatcher> matcher) {
this.matcher = matcher;
- this.location = new LocationImpl();
+ this.location = LocationFactory.create();
}
public Location getLocation() {
diff --git a/src/main/java/org/mockito/internal/matchers/Not.java b/src/main/java/org/mockito/internal/matchers/Not.java
index bd35eafbad..62b91b5c64 100644
--- a/src/main/java/org/mockito/internal/matchers/Not.java
+++ b/src/main/java/org/mockito/internal/matchers/Not.java
@@ -22,6 +22,11 @@ public boolean matches(Object actual) {
return !matcher.matches(actual);
}
+ @Override
+ public Class> type() {
+ return matcher.type();
+ }
+
@Override
public String toString() {
return "not(" + matcher + ")";
diff --git a/src/main/java/org/mockito/internal/matchers/NotNull.java b/src/main/java/org/mockito/internal/matchers/NotNull.java
index 2902f57061..f3f7fe7d05 100644
--- a/src/main/java/org/mockito/internal/matchers/NotNull.java
+++ b/src/main/java/org/mockito/internal/matchers/NotNull.java
@@ -5,20 +5,30 @@
package org.mockito.internal.matchers;
import java.io.Serializable;
+import java.util.Objects;
import org.mockito.ArgumentMatcher;
-public class NotNull implements ArgumentMatcher, Serializable {
+public class NotNull implements ArgumentMatcher, Serializable {
- public static final NotNull NOT_NULL = new NotNull();
+ public static final NotNull NOT_NULL = new NotNull<>(Object.class);
- private NotNull() {}
+ private final Class type;
+
+ public NotNull(Class type) {
+ this.type = Objects.requireNonNull(type);
+ }
@Override
public boolean matches(Object actual) {
return actual != null;
}
+ @Override
+ public Class type() {
+ return type;
+ }
+
@Override
public String toString() {
return "notNull()";
diff --git a/src/main/java/org/mockito/internal/matchers/Null.java b/src/main/java/org/mockito/internal/matchers/Null.java
index 69eee484a1..400170a5cc 100644
--- a/src/main/java/org/mockito/internal/matchers/Null.java
+++ b/src/main/java/org/mockito/internal/matchers/Null.java
@@ -5,20 +5,29 @@
package org.mockito.internal.matchers;
import java.io.Serializable;
+import java.util.Objects;
import org.mockito.ArgumentMatcher;
-public class Null implements ArgumentMatcher, Serializable {
+public class Null implements ArgumentMatcher, Serializable {
- public static final Null NULL = new Null();
+ public static final Null NULL = new Null<>(Object.class);
+ private final Class type;
- private Null() {}
+ public Null(Class type) {
+ this.type = Objects.requireNonNull(type);
+ }
@Override
public boolean matches(Object actual) {
return actual == null;
}
+ @Override
+ public Class type() {
+ return type;
+ }
+
@Override
public String toString() {
return "isNull()";
diff --git a/src/main/java/org/mockito/internal/matchers/Or.java b/src/main/java/org/mockito/internal/matchers/Or.java
index ed7bbdeb4d..7354814323 100644
--- a/src/main/java/org/mockito/internal/matchers/Or.java
+++ b/src/main/java/org/mockito/internal/matchers/Or.java
@@ -23,6 +23,13 @@ public boolean matches(Object actual) {
return m1.matches(actual) || m2.matches(actual);
}
+ @Override
+ public Class> type() {
+ return m1.type().isAssignableFrom(m2.type())
+ ? m1.type()
+ : m2.type().isAssignableFrom(m1.type()) ? m2.type() : ArgumentMatcher.super.type();
+ }
+
@Override
public String toString() {
return "or(" + m1 + ", " + m2 + ")";
diff --git a/src/main/java/org/mockito/internal/matchers/Same.java b/src/main/java/org/mockito/internal/matchers/Same.java
index 0e23c5cd46..fa116b53f0 100644
--- a/src/main/java/org/mockito/internal/matchers/Same.java
+++ b/src/main/java/org/mockito/internal/matchers/Same.java
@@ -22,6 +22,11 @@ public boolean matches(Object actual) {
return wanted == actual;
}
+ @Override
+ public Class> type() {
+ return wanted != null ? wanted.getClass() : ArgumentMatcher.super.type();
+ }
+
@Override
public String toString() {
return "same(" + ValuePrinter.print(wanted) + ")";
diff --git a/src/main/java/org/mockito/internal/matchers/VarargMatcher.java b/src/main/java/org/mockito/internal/matchers/VarargMatcher.java
deleted file mode 100644
index 43a27596f8..0000000000
--- a/src/main/java/org/mockito/internal/matchers/VarargMatcher.java
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Copyright (c) 2007 Mockito contributors
- * This program is made available under the terms of the MIT License.
- */
-package org.mockito.internal.matchers;
-
-import java.io.Serializable;
-
-/**
- * Internal interface that informs Mockito that the matcher is intended to capture varargs.
- * This information is needed when mockito collects the arguments.
- */
-public interface VarargMatcher extends Serializable {}
diff --git a/src/main/java/org/mockito/internal/progress/MockingProgressImpl.java b/src/main/java/org/mockito/internal/progress/MockingProgressImpl.java
index 991b5e4734..2585d32cfc 100644
--- a/src/main/java/org/mockito/internal/progress/MockingProgressImpl.java
+++ b/src/main/java/org/mockito/internal/progress/MockingProgressImpl.java
@@ -14,7 +14,7 @@
import org.mockito.internal.configuration.GlobalConfiguration;
import org.mockito.internal.debugging.Localized;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.debugging.LocationFactory;
import org.mockito.internal.exceptions.Reporter;
import org.mockito.internal.listeners.AutoCleanableListener;
import org.mockito.invocation.Location;
@@ -105,7 +105,7 @@ public VerificationMode pullVerificationMode() {
@Override
public void stubbingStarted() {
validateState();
- stubbingInProgress = new LocationImpl();
+ stubbingInProgress = LocationFactory.create();
}
@Override
diff --git a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java
index 5aedbb912e..832b1b155b 100644
--- a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java
+++ b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java
@@ -25,7 +25,6 @@
import org.mockito.stubbing.Stubbing;
import org.mockito.stubbing.ValidableAnswer;
-@SuppressWarnings("unchecked")
public class InvocationContainerImpl implements InvocationContainer, Serializable {
private static final long serialVersionUID = -5334301962749537177L;
@@ -36,7 +35,7 @@ public class InvocationContainerImpl implements InvocationContainer, Serializabl
private MatchableInvocation invocationForStubbing;
- public InvocationContainerImpl(MockCreationSettings mockSettings) {
+ public InvocationContainerImpl(MockCreationSettings> mockSettings) {
this.registeredInvocations = createRegisteredInvocations(mockSettings);
this.mockStrictness = mockSettings.getStrictness();
this.doAnswerStyleStubbing = new DoAnswerStyleStubbing();
@@ -51,14 +50,14 @@ public void resetInvocationForPotentialStubbing(MatchableInvocation invocationMa
this.invocationForStubbing = invocationMatcher;
}
- public void addAnswer(Answer answer, Strictness stubbingStrictness) {
+ public void addAnswer(Answer> answer, Strictness stubbingStrictness) {
registeredInvocations.removeLast();
addAnswer(answer, false, stubbingStrictness);
}
/** Adds new stubbed answer and returns the invocation matcher the answer was added to. */
public StubbedInvocationMatcher addAnswer(
- Answer answer, boolean isConsecutive, Strictness stubbingStrictness) {
+ Answer> answer, boolean isConsecutive, Strictness stubbingStrictness) {
Invocation invocation = invocationForStubbing.getInvocation();
mockingProgress().stubbingCompleted();
if (answer instanceof ValidableAnswer) {
@@ -79,7 +78,7 @@ public StubbedInvocationMatcher addAnswer(
}
}
- public void addConsecutiveAnswer(Answer answer) {
+ public void addConsecutiveAnswer(Answer> answer) {
addAnswer(answer, true, null);
}
@@ -143,18 +142,14 @@ public void clearInvocations() {
registeredInvocations.clear();
}
- /**
- * Stubbings in descending order, most recent first
- */
- public List getStubbingsDescending() {
- return (List) stubbed;
- }
-
/**
* Stubbings in ascending order, most recent last
*/
public Collection getStubbingsAscending() {
- List result = new LinkedList<>(stubbed);
+ List result;
+ synchronized (stubbed) {
+ result = new LinkedList<>(stubbed);
+ }
Collections.reverse(result);
return result;
}
@@ -163,13 +158,14 @@ public Object invokedMock() {
return invocationForStubbing.getInvocation().getMock();
}
- private RegisteredInvocations createRegisteredInvocations(MockCreationSettings mockSettings) {
+ private RegisteredInvocations createRegisteredInvocations(
+ MockCreationSettings> mockSettings) {
return mockSettings.isStubOnly()
? new SingleRegisteredInvocation()
: new DefaultRegisteredInvocations();
}
- public Answer findStubbedAnswer() {
+ public Answer> findStubbedAnswer() {
synchronized (stubbed) {
for (StubbedInvocationMatcher s : stubbed) {
if (invocationForStubbing.matches(s.getInvocation())) {
diff --git a/src/main/java/org/mockito/internal/stubbing/answers/CallsRealMethods.java b/src/main/java/org/mockito/internal/stubbing/answers/CallsRealMethods.java
index bab007bbb0..ae16991af8 100644
--- a/src/main/java/org/mockito/internal/stubbing/answers/CallsRealMethods.java
+++ b/src/main/java/org/mockito/internal/stubbing/answers/CallsRealMethods.java
@@ -17,10 +17,10 @@
/**
* Optional Answer that adds partial mocking support
*
- * {@link Answer} can be used to define the return values of unstubbed invocations.
+ * {@link Answer} can be used to define the return values of un-stubbed invocations.
*
* This implementation can be helpful when working with legacy code.
- * When this implementation is used, unstubbed methods will delegate to the real implementation.
+ * When this implementation is used, un-stubbed methods will delegate to the real implementation.
* This is a way to create a partial mock object that calls real methods by default.
*
* As usual you are going to read the partial mock warning :
diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java
index fa6a72f53d..8b64a16916 100644
--- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java
+++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java
@@ -8,7 +8,6 @@
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;
@@ -16,8 +15,6 @@
final class RetrieveGenericsForDefaultAnswers {
- private static final MockitoCore MOCKITO_CORE = new MockitoCore();
-
static Object returnTypeForMockWithCorrectGenerics(
InvocationOnMock invocation, AnswerCallback answerCallback) {
Class> type = invocation.getMethod().getReturnType();
@@ -38,7 +35,9 @@ static Object returnTypeForMockWithCorrectGenerics(
}
if (type != null) {
- if (!MOCKITO_CORE.isTypeMockable(type)) {
+ final MockCreationSettings> mockSettings =
+ MockUtil.getMockSettings(invocation.getMock());
+ if (!MockUtil.typeMockabilityOf(type, mockSettings.getMockMaker()).mockable()) {
return null;
}
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 097b11b6e2..8356c1b79b 100644
--- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java
+++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java
@@ -5,6 +5,7 @@
package org.mockito.internal.stubbing.defaultanswers;
import static org.mockito.Mockito.withSettings;
+import static org.mockito.internal.util.MockUtil.typeMockabilityOf;
import java.io.IOException;
import java.io.Serializable;
@@ -50,9 +51,10 @@ public Object answer(InvocationOnMock invocation) throws Throwable {
GenericMetadataSupport returnTypeGenericMetadata =
actualParameterizedType(invocation.getMock())
.resolveGenericReturnType(invocation.getMethod());
+ MockCreationSettings> mockSettings = MockUtil.getMockSettings(invocation.getMock());
Class> rawType = returnTypeGenericMetadata.rawType();
- if (!mockitoCore().isTypeMockable(rawType)) {
+ if (!typeMockabilityOf(rawType, mockSettings.getMockMaker()).mockable()) {
if (invocation.getMethod().getReturnType().equals(rawType)) {
return delegate().answer(invocation);
} else {
@@ -119,7 +121,7 @@ private Object newDeepStubMock(
private MockSettings withSettingsUsing(
GenericMetadataSupport returnTypeGenericMetadata,
- MockCreationSettings parentMockSettings) {
+ MockCreationSettings> parentMockSettings) {
MockSettings mockSettings =
returnTypeGenericMetadata.hasRawExtraInterfaces()
? withSettings()
@@ -127,7 +129,8 @@ private MockSettings withSettingsUsing(
: withSettings();
return propagateSerializationSettings(mockSettings, parentMockSettings)
- .defaultAnswer(returnsDeepStubsAnswerUsing(returnTypeGenericMetadata));
+ .defaultAnswer(returnsDeepStubsAnswerUsing(returnTypeGenericMetadata))
+ .mockMaker(parentMockSettings.getMockMaker());
}
private MockSettings propagateSerializationSettings(
diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java
index ad8e7e2466..20e9d85afe 100644
--- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java
+++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java
@@ -8,6 +8,8 @@
import static org.mockito.internal.util.ObjectMethodsGuru.isToStringMethod;
import java.io.Serializable;
+import java.time.Duration;
+import java.time.Period;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -17,12 +19,20 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
-import org.mockito.internal.util.JavaEightUtil;
+import java.util.stream.DoubleStream;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+import java.util.stream.Stream;
+
import org.mockito.internal.util.MockUtil;
import org.mockito.internal.util.Primitives;
import org.mockito.invocation.InvocationOnMock;
@@ -129,26 +139,26 @@ Object returnValueFor(Class> type) {
return new TreeMap<>();
} else if (type == LinkedHashMap.class) {
return new LinkedHashMap<>();
- } else if ("java.util.Optional".equals(type.getName())) {
- return JavaEightUtil.emptyOptional();
- } else if ("java.util.OptionalDouble".equals(type.getName())) {
- return JavaEightUtil.emptyOptionalDouble();
- } else if ("java.util.OptionalInt".equals(type.getName())) {
- return JavaEightUtil.emptyOptionalInt();
- } else if ("java.util.OptionalLong".equals(type.getName())) {
- return JavaEightUtil.emptyOptionalLong();
- } else if ("java.util.stream.Stream".equals(type.getName())) {
- return JavaEightUtil.emptyStream();
- } else if ("java.util.stream.DoubleStream".equals(type.getName())) {
- return JavaEightUtil.emptyDoubleStream();
- } else if ("java.util.stream.IntStream".equals(type.getName())) {
- return JavaEightUtil.emptyIntStream();
- } else if ("java.util.stream.LongStream".equals(type.getName())) {
- return JavaEightUtil.emptyLongStream();
- } else if ("java.time.Duration".equals(type.getName())) {
- return JavaEightUtil.emptyDuration();
- } else if ("java.time.Period".equals(type.getName())) {
- return JavaEightUtil.emptyPeriod();
+ } else if (type == Optional.class) {
+ return Optional.empty();
+ } else if (type == OptionalDouble.class) {
+ return OptionalDouble.empty();
+ } else if (type == OptionalInt.class) {
+ return OptionalInt.empty();
+ } else if (type == OptionalLong.class) {
+ return OptionalLong.empty();
+ } else if (type == Stream.class) {
+ return Stream.empty();
+ } else if (type == DoubleStream.class) {
+ return DoubleStream.empty();
+ } else if (type == IntStream.class) {
+ return IntStream.empty();
+ } else if (type == LongStream.class) {
+ return LongStream.empty();
+ } else if (type == Duration.class) {
+ return Duration.ZERO;
+ } else if (type == Period.class) {
+ return Period.ZERO;
}
// Let's not care about the rest of collections.
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 0ef6f0d954..c155780916 100755
--- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java
+++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java
@@ -8,7 +8,9 @@
import org.mockito.Mockito;
import org.mockito.internal.creation.MockSettingsImpl;
+import org.mockito.internal.util.MockUtil;
import org.mockito.invocation.InvocationOnMock;
+import org.mockito.mock.MockCreationSettings;
import org.mockito.stubbing.Answer;
public class ReturnsMocks implements Answer, Serializable {
@@ -33,9 +35,14 @@ public Object apply(Class> type) {
return null;
}
+ MockCreationSettings> mockSettings =
+ MockUtil.getMockSettings(invocation.getMock());
+
return Mockito.mock(
type,
- new MockSettingsImpl().defaultAnswer(ReturnsMocks.this));
+ new MockSettingsImpl<>()
+ .defaultAnswer(ReturnsMocks.this)
+ .mockMaker(mockSettings.getMockMaker()));
}
});
}
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 2402f364a6..d538784a94 100644
--- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java
+++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java
@@ -8,23 +8,29 @@
import static org.mockito.internal.util.ObjectMethodsGuru.isToStringMethod;
import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
import org.mockito.Mockito;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.creation.MockSettingsImpl;
+import org.mockito.internal.creation.bytebuddy.MockAccess;
+import org.mockito.internal.debugging.LocationFactory;
+import org.mockito.internal.util.MockUtil;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.invocation.Location;
+import org.mockito.mock.MockCreationSettings;
import org.mockito.stubbing.Answer;
/**
* Optional Answer that can be used with
* {@link Mockito#mock(Class, Answer)}
*
- * This implementation can be helpful when working with legacy code. Unstubbed
+ * This implementation can be helpful when working with legacy code. Un-stubbed
* methods often return null. If your code uses the object returned by an
- * unstubbed call you get a NullPointerException. This implementation of
+ * un-stubbed call, you get a NullPointerException. This implementation of
* Answer returns SmartNulls instead of nulls.
* SmartNull gives nicer exception message than NPE because it points out the
- * line where unstubbed method was called. You just click on the stack trace.
+ * line where un-stubbed method was called. You just click on the stack trace.
*
* ReturnsSmartNulls first tries to return ordinary return values (see
* {@link ReturnsMoreEmptyValues}) then it tries to return SmartNull. If the
@@ -56,8 +62,16 @@ public Object apply(Class> type) {
return null;
}
+ MockCreationSettings> mockSettings =
+ MockUtil.getMockSettings(invocation.getMock());
+ Answer> defaultAnswer =
+ new ThrowsSmartNullPointer(invocation, LocationFactory.create());
+
return Mockito.mock(
- type, new ThrowsSmartNullPointer(invocation, new LocationImpl()));
+ type,
+ new MockSettingsImpl<>()
+ .defaultAnswer(defaultAnswer)
+ .mockMaker(mockSettings.getMockMaker()));
}
});
}
@@ -76,11 +90,30 @@ private static class ThrowsSmartNullPointer implements Answer {
@Override
public Object answer(InvocationOnMock currentInvocation) throws Throwable {
if (isToStringMethod(currentInvocation.getMethod())) {
- return "SmartNull returned by this unstubbed method call on a mock:\n"
+ return "SmartNull returned by this un-stubbed method call on a mock:\n"
+ unstubbedInvocation;
+ } else if (isMethodOf(
+ MockAccess.class, currentInvocation.getMock(), currentInvocation.getMethod())) {
+ /* The MockAccess methods should be called directly */
+ return currentInvocation.callRealMethod();
}
throw smartNullPointerException(unstubbedInvocation.toString(), location);
}
+
+ private static boolean isMethodOf(Class> clazz, Object instance, Method method) {
+ if (!clazz.isInstance(instance)) {
+ return false;
+ }
+
+ for (Method m : clazz.getDeclaredMethods()) {
+ if (m.getName().equalsIgnoreCase(method.getName())
+ && Arrays.equals(m.getParameterTypes(), method.getParameterTypes())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
}
diff --git a/src/main/java/org/mockito/internal/util/JavaEightUtil.java b/src/main/java/org/mockito/internal/util/JavaEightUtil.java
deleted file mode 100644
index 20c6e80c3b..0000000000
--- a/src/main/java/org/mockito/internal/util/JavaEightUtil.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (c) 2016 Mockito contributors
- * This program is made available under the terms of the MIT License.
- */
-package org.mockito.internal.util;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
-import org.mockito.creation.instance.InstantiationException;
-
-/**
- * Helper class to work with features that were introduced in Java versions after 1.5.
- * This class uses reflection in most places to avoid coupling with a newer JDK.
- */
-public final class JavaEightUtil {
-
- // No need for volatile, these optionals are already safe singletons.
- private static Object emptyOptional;
- private static Object emptyOptionalDouble;
- private static Object emptyOptionalInt;
- private static Object emptyOptionalLong;
- private static Object emptyDuration;
- private static Object emptyPeriod;
-
- private JavaEightUtil() {
- // utility class
- }
-
- /**
- * Creates an empty Optional using reflection to stay backwards-compatible with older JDKs.
- *
- * @return an empty Optional.
- */
- public static Object emptyOptional() {
- // no need for double-checked locking
- if (emptyOptional != null) {
- return emptyOptional;
- }
-
- return emptyOptional = invokeNullaryFactoryMethod("java.util.Optional", "empty");
- }
-
- /**
- * Creates an empty OptionalDouble using reflection to stay backwards-compatible with older JDKs.
- *
- * @return an empty OptionalDouble.
- */
- public static Object emptyOptionalDouble() {
- // no need for double-checked locking
- if (emptyOptionalDouble != null) {
- return emptyOptionalDouble;
- }
-
- return emptyOptionalDouble =
- invokeNullaryFactoryMethod("java.util.OptionalDouble", "empty");
- }
-
- /**
- * Creates an empty OptionalInt using reflection to stay backwards-compatible with older JDKs.
- *
- * @return an empty OptionalInt.
- */
- public static Object emptyOptionalInt() {
- // no need for double-checked locking
- if (emptyOptionalInt != null) {
- return emptyOptionalInt;
- }
-
- return emptyOptionalInt = invokeNullaryFactoryMethod("java.util.OptionalInt", "empty");
- }
-
- /**
- * Creates an empty OptionalLong using reflection to stay backwards-compatible with older JDKs.
- *
- * @return an empty OptionalLong.
- */
- public static Object emptyOptionalLong() {
- // no need for double-checked locking
- if (emptyOptionalLong != null) {
- return emptyOptionalLong;
- }
-
- return emptyOptionalLong = invokeNullaryFactoryMethod("java.util.OptionalLong", "empty");
- }
-
- /**
- * Creates an empty Stream using reflection to stay backwards-compatible with older JDKs.
- *
- * @return an empty Stream.
- */
- public static Object emptyStream() {
- // note: the empty stream can not be stored as a singleton.
- return invokeNullaryFactoryMethod("java.util.stream.Stream", "empty");
- }
-
- /**
- * Creates an empty DoubleStream using reflection to stay backwards-compatible with older JDKs.
- *
- * @return an empty DoubleStream.
- */
- public static Object emptyDoubleStream() {
- // note: the empty stream can not be stored as a singleton.
- return invokeNullaryFactoryMethod("java.util.stream.DoubleStream", "empty");
- }
-
- /**
- * Creates an empty IntStream using reflection to stay backwards-compatible with older JDKs.
- *
- * @return an empty IntStream.
- */
- public static Object emptyIntStream() {
- // note: the empty stream can not be stored as a singleton.
- return invokeNullaryFactoryMethod("java.util.stream.IntStream", "empty");
- }
-
- /**
- * Creates an empty LongStream using reflection to stay backwards-compatible with older JDKs.
- *
- * @return an empty LongStream.
- */
- public static Object emptyLongStream() {
- // note: the empty stream can not be stored as a singleton.
- return invokeNullaryFactoryMethod("java.util.stream.LongStream", "empty");
- }
-
- /**
- * Creates an empty Duration using reflection to stay backwards-compatible with older JDKs.
- *
- * @return an empty (ZERO) Duration.
- */
- public static Object emptyDuration() {
- // no need for double-checked locking
- if (emptyDuration != null) {
- return emptyDuration;
- }
-
- return emptyDuration = getStaticFieldValue("java.time.Duration", "ZERO");
- }
-
- /**
- * Creates an empty Period using reflection to stay backwards-compatible with older JDKs.
- *
- * @return an empty (ZERO) Period.
- */
- public static Object emptyPeriod() {
- // no need for double-checked locking
- if (emptyPeriod != null) {
- return emptyPeriod;
- }
-
- return emptyPeriod = getStaticFieldValue("java.time.Period", "ZERO");
- }
-
- /**
- * Invokes a nullary static factory method using reflection to stay backwards-compatible with older JDKs.
- *
- * @param fqcn The fully qualified class name of the type to be produced.
- * @param methodName The name of the factory method.
- * @return the object produced.
- */
- private static Object invokeNullaryFactoryMethod(final String fqcn, final String methodName) {
- try {
- final Method method = getMethod(fqcn, methodName);
- return method.invoke(null);
- // any exception is really unexpected since the type name has
- // already been verified
- } catch (final Exception e) {
- throw new InstantiationException(
- String.format("Could not create %s#%s(): %s", fqcn, methodName, e), e);
- }
- }
-
- /**
- * Gets a value of the classes' field using reflection to stay backwards-compatible with older JDKs.
- *
- * @param fqcn The fully qualified class name of the type to be produced.
- * @param fieldName The name of th classes' field which value is going to be returned.
- * @return the restored value.
- */
- private static Object getStaticFieldValue(final String fqcn, final String fieldName) {
- try {
- final Class> type = getClass(fqcn);
- final Field field = type.getField(fieldName);
- return field.get(null);
- // any exception is really unexpected since the type name has
- // already been verified
- } catch (Exception e) {
- throw new InstantiationException(
- String.format("Could not get %s#%s(): %s", fqcn, fieldName, e), e);
- }
- }
-
- /**
- * Returns the {@code Class} object associated with the class or interface with the given string name.
- *
- * @param fqcn The fully qualified class name of the type to be produced.
- * @return the Class object for the class with the specified name.
- */
- private static Class> getClass(String fqcn) {
- try {
- return Class.forName(fqcn);
- // any exception is really unexpected since the type name has
- // already been verified
- } catch (ClassNotFoundException e) {
- throw new InstantiationException(String.format("Could not find %s: %s", fqcn, e), e);
- }
- }
-
- /**
- * Returns a Method object that reflects the specified public member method of the class or interface represented by the fully qualified class name.
- *
- * @param fqcn The fully qualified class name of the type to be produced.
- * @param methodName The name of the method.
- * @param parameterClasses The list of parameters.
- * @return The Method object that matches the specified name and parameterTypes.
- */
- private static Method getMethod(
- final String fqcn, final String methodName, final Class>... parameterClasses) {
- try {
- final Class> type = getClass(fqcn);
- return type.getMethod(methodName, parameterClasses);
- } catch (Exception e) {
- throw new InstantiationException(
- String.format("Could not find %s#%s(): %s", fqcn, methodName, e), e);
- }
- }
-}
diff --git a/src/main/java/org/mockito/internal/util/MockCreationValidator.java b/src/main/java/org/mockito/internal/util/MockCreationValidator.java
index 7baa4e252f..9db6b36ea3 100644
--- a/src/main/java/org/mockito/internal/util/MockCreationValidator.java
+++ b/src/main/java/org/mockito/internal/util/MockCreationValidator.java
@@ -18,8 +18,8 @@
@SuppressWarnings("unchecked")
public class MockCreationValidator {
- public void validateType(Class> classToMock) {
- TypeMockability typeMockability = MockUtil.typeMockabilityOf(classToMock);
+ public void validateType(Class> classToMock, String mockMaker) {
+ TypeMockability typeMockability = MockUtil.typeMockabilityOf(classToMock, mockMaker);
if (!typeMockability.mockable()) {
throw cannotMockClass(classToMock, typeMockability.nonMockableReason());
}
diff --git a/src/main/java/org/mockito/internal/util/MockUtil.java b/src/main/java/org/mockito/internal/util/MockUtil.java
index 2159608623..0d80f6e195 100644
--- a/src/main/java/org/mockito/internal/util/MockUtil.java
+++ b/src/main/java/org/mockito/internal/util/MockUtil.java
@@ -7,6 +7,7 @@
import org.mockito.MockedConstruction;
import org.mockito.Mockito;
import org.mockito.exceptions.misusing.NotAMockException;
+import org.mockito.internal.configuration.plugins.DefaultMockitoPlugins;
import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.internal.creation.settings.CreationSettings;
import org.mockito.internal.stubbing.InvocationContainerImpl;
@@ -18,6 +19,9 @@
import org.mockito.plugins.MockMaker.TypeMockability;
import org.mockito.plugins.MockResolver;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import static org.mockito.internal.handler.MockHandlerFactory.createMockHandler;
@@ -25,15 +29,57 @@
@SuppressWarnings("unchecked")
public class MockUtil {
- private static final MockMaker mockMaker = Plugins.getMockMaker();
+ private static final MockMaker defaultMockMaker = Plugins.getMockMaker();
+ private static final Map, MockMaker> mockMakers =
+ new ConcurrentHashMap<>(
+ Collections.singletonMap(defaultMockMaker.getClass(), defaultMockMaker));
private MockUtil() {}
- public static TypeMockability typeMockabilityOf(Class> type) {
- return mockMaker.isTypeMockable(type);
+ private static MockMaker getMockMaker(String mockMaker) {
+ if (mockMaker == null) {
+ return defaultMockMaker;
+ }
+
+ String typeName;
+ if (DefaultMockitoPlugins.MOCK_MAKER_ALIASES.contains(mockMaker)) {
+ typeName = DefaultMockitoPlugins.getDefaultPluginClass(mockMaker);
+ } else {
+ typeName = mockMaker;
+ }
+
+ Class extends MockMaker> type;
+ // Using the context class loader because PluginInitializer.loadImpl is using it as well.
+ // Personally, I am suspicious whether the context class loader is a good choice in either
+ // of these cases.
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ if (loader == null) {
+ loader = ClassLoader.getSystemClassLoader();
+ }
+ try {
+ type = loader.loadClass(typeName).asSubclass(MockMaker.class);
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to load MockMaker: " + mockMaker, e);
+ }
+
+ return mockMakers.computeIfAbsent(
+ type,
+ t -> {
+ try {
+ return t.getDeclaredConstructor().newInstance();
+ } catch (Exception e) {
+ throw new IllegalStateException(
+ "Failed to construct MockMaker: " + t.getName(), e);
+ }
+ });
+ }
+
+ public static TypeMockability typeMockabilityOf(Class> type, String mockMaker) {
+ return getMockMaker(mockMaker).isTypeMockable(type);
}
public static T createMock(MockCreationSettings settings) {
+ MockMaker mockMaker = getMockMaker(settings.getMockMaker());
MockHandler mockHandler = createMockHandler(settings);
Object spiedInstance = settings.getSpiedInstance();
@@ -62,17 +108,11 @@ public static void resetMock(Object mock) {
MockHandler newHandler = createMockHandler(settings);
mock = resolve(mock);
- mockMaker.resetMock(mock, newHandler, settings);
+ getMockMaker(settings.getMockMaker()).resetMock(mock, newHandler, settings);
}
public static MockHandler> getMockHandler(Object mock) {
- if (mock == null) {
- throw new NotAMockException("Argument should be a mock, but is null!");
- }
-
- mock = resolve(mock);
-
- MockHandler handler = mockMaker.getHandler(mock);
+ MockHandler handler = getMockHandlerOrNull(mock);
if (handler != null) {
return handler;
} else {
@@ -104,10 +144,24 @@ public static boolean isMock(Object mock) {
if (mock == null) {
return false;
}
+ return getMockHandlerOrNull(mock) != null;
+ }
+
+ private static MockHandler> getMockHandlerOrNull(Object mock) {
+ if (mock == null) {
+ throw new NotAMockException("Argument should be a mock, but is null!");
+ }
mock = resolve(mock);
- return mockMaker.getHandler(mock) != null;
+ for (MockMaker mockMaker : mockMakers.values()) {
+ MockHandler> handler = mockMaker.getHandler(mock);
+ if (handler != null) {
+ assert getMockMaker(handler.getMockSettings().getMockMaker()) == mockMaker;
+ return handler;
+ }
+ }
+ return null;
}
private static Object resolve(Object mock) {
@@ -143,6 +197,7 @@ public static MockCreationSettings getMockSettings(Object mock) {
public static MockMaker.StaticMockControl createStaticMock(
Class type, MockCreationSettings settings) {
+ MockMaker mockMaker = getMockMaker(settings.getMockMaker());
MockHandler handler = createMockHandler(settings);
return mockMaker.createStaticMock(type, settings, handler);
}
@@ -153,11 +208,13 @@ public static MockMaker.ConstructionMockControl createConstructionMock(
MockedConstruction.MockInitializer mockInitializer) {
Function> handlerFactory =
context -> createMockHandler(settingsFactory.apply(context));
- return mockMaker.createConstructionMock(
+ return defaultMockMaker.createConstructionMock(
type, settingsFactory, handlerFactory, mockInitializer);
}
public static void clearAllCaches() {
- mockMaker.clearAllCaches();
+ for (MockMaker mockMaker : mockMakers.values()) {
+ mockMaker.clearAllCaches();
+ }
}
}
diff --git a/src/main/java/org/mockito/internal/util/Platform.java b/src/main/java/org/mockito/internal/util/Platform.java
index fde59fbbe9..6ff0378280 100644
--- a/src/main/java/org/mockito/internal/util/Platform.java
+++ b/src/main/java/org/mockito/internal/util/Platform.java
@@ -7,15 +7,9 @@
import static org.mockito.internal.util.StringUtil.join;
import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public abstract class Platform {
- private static final Pattern JAVA_8_RELEASE_VERSION_SCHEME =
- Pattern.compile("1\\.8\\.0_(\\d+)(?:-ea)?(?:-b\\d+)?");
- private static final Pattern JAVA_8_DEV_VERSION_SCHEME =
- Pattern.compile("1\\.8\\.0b\\d+_u(\\d+)");
public static final String JAVA_VERSION = System.getProperty("java.specification.version");
public static final String JVM_VERSION = System.getProperty("java.runtime.version");
public static final String JVM_VENDOR = System.getProperty("java.vm.vendor");
@@ -68,31 +62,6 @@ public static String describe() {
return description;
}
- public static boolean isJava8BelowUpdate45() {
- if (JVM_VERSION == null) {
- return false;
- } else {
- return isJava8BelowUpdate45(JVM_VERSION);
- }
- }
-
- static boolean isJava8BelowUpdate45(String jvmVersion) {
- Matcher matcher = JAVA_8_RELEASE_VERSION_SCHEME.matcher(jvmVersion);
- if (matcher.matches()) {
- int update = Integer.parseInt(matcher.group(1));
- return update < 45;
- }
-
- matcher = JAVA_8_DEV_VERSION_SCHEME.matcher(jvmVersion);
- if (matcher.matches()) {
- int update = Integer.parseInt(matcher.group(1));
- return update < 45;
- }
-
- matcher = Pattern.compile("1\\.8\\.0-b\\d+").matcher(jvmVersion);
- return matcher.matches();
- }
-
public static String warnForVM(
String vmName1, String warnMessage1, String vmName2, String warnMessage2) {
return warnForVM(JVM_NAME, vmName1, warnMessage1, vmName2, warnMessage2);
diff --git a/src/main/java/org/mockito/internal/util/io/IOUtil.java b/src/main/java/org/mockito/internal/util/io/IOUtil.java
index 1223cbe93e..f77dfda1c3 100644
--- a/src/main/java/org/mockito/internal/util/io/IOUtil.java
+++ b/src/main/java/org/mockito/internal/util/io/IOUtil.java
@@ -7,11 +7,12 @@
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
-import java.io.FileWriter;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.PrintWriter;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
@@ -24,12 +25,12 @@
public final class IOUtil {
/**
- * Writes text to file
+ * Writes text to file in UTF-8.
*/
public static void writeText(String text, File output) {
- PrintWriter pw = null;
+ OutputStreamWriter pw = null;
try {
- pw = new PrintWriter(new FileWriter(output));
+ pw = new OutputStreamWriter(new FileOutputStream(output), StandardCharsets.UTF_8);
pw.write(text);
} catch (Exception e) {
throw new MockitoException("Problems writing text to file: " + output, e);
@@ -38,9 +39,12 @@ public static void writeText(String text, File output) {
}
}
+ /**
+ * Reads all lines from the given stream. Uses UTF-8.
+ */
public static Collection readLines(InputStream is) {
List out = new LinkedList<>();
- BufferedReader r = new BufferedReader(new InputStreamReader(is));
+ BufferedReader r = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
String line;
try {
while ((line = r.readLine()) != null) {
diff --git a/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java b/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java
index 3f104d1f02..7d51a6bb1b 100644
--- a/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java
+++ b/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java
@@ -259,7 +259,11 @@ public int compare(Constructor> constructorA, Constructor> constructorB) {
private int countMockableParams(Constructor> constructor) {
int constructorMockableParamsSize = 0;
for (Class> aClass : constructor.getParameterTypes()) {
- if (MockUtil.typeMockabilityOf(aClass).mockable()) {
+ // The argResolver already knows the concrete types it can provide.
+ // Instead of checking for mockability, I think it would be better to
+ // ask the argResolver whether it can resolve this type.
+ // Anyway, I keep it for now to avoid breaking any existing code.
+ if (MockUtil.typeMockabilityOf(aClass, null).mockable()) {
constructorMockableParamsSize++;
}
}
diff --git a/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java b/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java
index 0a972d8019..61b3f06c46 100644
--- a/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java
+++ b/src/main/java/org/mockito/internal/util/reflection/InstrumentationMemberAccessor.java
@@ -188,7 +188,16 @@ public Object newInstance(
onConstruction.invoke(
() -> {
try {
- return DISPATCHER.invokeWithArguments(handle, arguments);
+ // Use handle.asFixedArity() to handle varargs bindings properly
+ // (as by default Java will create method handle with
+ // asVarargsCollector), fe:
+ //
+ // private static class Varargs {
+ // Varargs(String whatever, Observer... observers) {
+ // }
+ // }
+ return DISPATCHER.invokeWithArguments(
+ handle.asFixedArity(), arguments);
} catch (Throwable throwable) {
thrown.set(true);
return throwable;
@@ -235,6 +244,9 @@ public Object invoke(Method method, Object target, Object... arguments)
if (!Modifier.isStatic(method.getModifiers())) {
handle = handle.bindTo(target);
}
+ if (handle.isVarargsCollector()) {
+ handle = handle.asFixedArity();
+ }
try {
return DISPATCHER.invokeWithArguments(handle, arguments);
} catch (Throwable t) {
@@ -369,17 +381,23 @@ private static void assureArguments(
throw new IllegalArgumentException("Cannot access " + target + " on " + owner);
}
}
- if (types.length != values.length) {
+
+ Object[] args = values;
+ if (args == null) {
+ args = new Object[0];
+ }
+
+ if (types.length != args.length) {
throw new IllegalArgumentException(
"Incorrect number of arguments for "
+ target
+ ": expected "
+ types.length
+ " but recevied "
- + values.length);
+ + args.length);
}
- for (int index = 0; index < values.length; index++) {
- if (values[index] == null) {
+ for (int index = 0; index < args.length; index++) {
+ if (args[index] == null) {
if (types[index].isPrimitive()) {
throw new IllegalArgumentException(
"Cannot assign null to primitive type "
@@ -391,10 +409,10 @@ private static void assureArguments(
}
} else {
Class> resolved = WRAPPERS.getOrDefault(types[index], types[index]);
- if (!resolved.isAssignableFrom(values[index].getClass())) {
+ if (!resolved.isAssignableFrom(args[index].getClass())) {
throw new IllegalArgumentException(
"Cannot assign value of type "
- + values[index].getClass()
+ + args[index].getClass()
+ " to "
+ resolved
+ " for "
diff --git a/src/main/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingTool.java b/src/main/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingTool.java
index 060ea0913e..1959147fc1 100644
--- a/src/main/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingTool.java
+++ b/src/main/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingTool.java
@@ -4,6 +4,8 @@
*/
package org.mockito.internal.verification.argumentmatching;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -15,7 +17,7 @@
import org.mockito.ArgumentMatcher;
import org.mockito.internal.matchers.ContainsExtraTypeInfo;
-@SuppressWarnings("unchecked")
+@SuppressWarnings("rawtypes")
public class ArgumentMatchingTool {
private ArgumentMatchingTool() {}
@@ -42,6 +44,28 @@ && toStringEquals(m, arguments[i])
return suspicious.toArray(new Integer[0]);
}
+ /**
+ * Returns indexes of arguments not matching the provided matchers.
+ */
+ public static List getNotMatchingArgsIndexes(
+ List matchers, Object[] arguments) {
+ if (matchers.size() != arguments.length) {
+ return Collections.emptyList();
+ }
+
+ List nonMatching = new ArrayList<>();
+ int i = 0;
+ for (ArgumentMatcher m : matchers) {
+ if (!safelyMatches(m, arguments[i])) {
+ nonMatching.add(i);
+ }
+
+ i++;
+ }
+
+ return nonMatching;
+ }
+
private static boolean safelyMatches(ArgumentMatcher m, Object arg) {
try {
return m.matches(arg);
diff --git a/src/main/java/org/mockito/internal/verification/checkers/MissingInvocationChecker.java b/src/main/java/org/mockito/internal/verification/checkers/MissingInvocationChecker.java
index 9ceb483008..03c3d8b462 100644
--- a/src/main/java/org/mockito/internal/verification/checkers/MissingInvocationChecker.java
+++ b/src/main/java/org/mockito/internal/verification/checkers/MissingInvocationChecker.java
@@ -52,7 +52,11 @@ public static void checkMissingInvocation(
invocations.stream().map(Invocation::getLocation).collect(Collectors.toList());
throw argumentsAreDifferent(
- smartPrinter.getWanted(), smartPrinter.getActuals(), actualLocations);
+ similar,
+ wanted,
+ smartPrinter.getWanted(),
+ smartPrinter.getActuals(),
+ actualLocations);
}
public static void checkMissingInvocation(
diff --git a/src/main/java/org/mockito/junit/MockitoJUnitRunner.java b/src/main/java/org/mockito/junit/MockitoJUnitRunner.java
index f7f4e1b928..bfc938e2dc 100644
--- a/src/main/java/org/mockito/junit/MockitoJUnitRunner.java
+++ b/src/main/java/org/mockito/junit/MockitoJUnitRunner.java
@@ -34,7 +34,7 @@
* See {@link UnnecessaryStubbingException}.
* Similar to JUnit rules, the runner also reports stubbing argument mismatches as console warnings
* (see {@link MockitoHint}).
- * To opt-out from this feature, use {@code}@RunWith(MockitoJUnitRunner.Silent.class){@code}
+ * To opt-out from this feature, use {@code @RunWith(MockitoJUnitRunner.Silent.class)}
*
* Initializes mocks annotated with {@link Mock},
* so that explicit usage of {@link MockitoAnnotations#openMocks(Object)} is not necessary.
@@ -68,7 +68,7 @@
* }
*
*
- * If you would like to take advantage of Mockito JUnit runner features
+ * If you would like to take advantage of Mockito JUnit runner features,
* but you cannot use the runner there is a solution!
* {@link MockitoSession} API is intended to offer cleaner tests and improved debuggability
* to users that cannot use Mockito's built-in JUnit support (runner or the rule).
@@ -154,7 +154,7 @@ public MockitoJUnitRunner(Class> klass) throws InvocationTargetException {
this(new StrictRunner(new RunnerFactory().createStrict(klass), klass));
}
- MockitoJUnitRunner(InternalRunner runner) throws InvocationTargetException {
+ MockitoJUnitRunner(InternalRunner runner) {
this.runner = runner;
}
diff --git a/src/main/java/org/mockito/mock/MockCreationSettings.java b/src/main/java/org/mockito/mock/MockCreationSettings.java
index f7f0b96028..949af03b2e 100644
--- a/src/main/java/org/mockito/mock/MockCreationSettings.java
+++ b/src/main/java/org/mockito/mock/MockCreationSettings.java
@@ -4,6 +4,7 @@
*/
package org.mockito.mock;
+import java.lang.reflect.Type;
import java.util.List;
import java.util.Set;
@@ -12,6 +13,7 @@
import org.mockito.listeners.InvocationListener;
import org.mockito.listeners.StubbingLookupListener;
import org.mockito.listeners.VerificationStartedListener;
+import org.mockito.plugins.MockMaker;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
@@ -26,6 +28,11 @@ public interface MockCreationSettings {
*/
Class getTypeToMock();
+ /**
+ * The generic type of the mock, if any.
+ */
+ Type getGenericTypeToMock();
+
/**
* the extra interfaces the mock object should implement.
*/
@@ -135,4 +142,13 @@ public interface MockCreationSettings {
* @since 4.6.0
*/
Strictness getStrictness();
+
+ /**
+ * Returns the {@link MockMaker} which shall be used to create the mock.
+ * When the return value is {@code null}, the default shall be used.
+ *
+ * @see MockSettings#mockMaker(String)
+ * @since 4.8.0
+ */
+ String getMockMaker();
}
diff --git a/src/main/java/org/mockito/plugins/MockMaker.java b/src/main/java/org/mockito/plugins/MockMaker.java
index 93a87ef0a5..c0b1cbcd2d 100644
--- a/src/main/java/org/mockito/plugins/MockMaker.java
+++ b/src/main/java/org/mockito/plugins/MockMaker.java
@@ -4,6 +4,7 @@
*/
package org.mockito.plugins;
+import org.mockito.MockSettings;
import org.mockito.MockedConstruction;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.invocation.MockHandler;
@@ -45,6 +46,19 @@
* Note that if several mockito-extensions/org.mockito.plugins.MockMaker
files exists in the classpath
* Mockito will only use the first returned by the standard {@link ClassLoader#getResource} mechanism.
*
+ *
Using the MockSettings of individual mocks
+ *
+ * If you want to use a {@code MockMaker} only for a specific mock,
+ * you can specify it using {@link MockSettings#mockMaker(String)}.
+ *
+ * // Use a built-in mock maker
+ * Object mock = Mockito.mock(Object.class, Mockito.withSettings()
+ * .mockMaker(MockMakers.INLINE));
+ * // Or load a mock maker using a fully qualified class name
+ * Object mock = Mockito.mock(Object.class, Mockito.withSettings()
+ * .mockMaker("org.awesome.mockito.AwesomeMockMaker"));
+ *
+ *
* @see org.mockito.mock.MockCreationSettings
* @see org.mockito.invocation.MockHandler
* @since 1.9.5
diff --git a/src/main/java/org/mockito/verification/VerificationWithTimeout.java b/src/main/java/org/mockito/verification/VerificationWithTimeout.java
index ad110d0cce..3e70d116bb 100644
--- a/src/main/java/org/mockito/verification/VerificationWithTimeout.java
+++ b/src/main/java/org/mockito/verification/VerificationWithTimeout.java
@@ -12,8 +12,6 @@
*
* verify(mock, timeout(100).times(5)).foo();
*
- * verify(mock, timeout(100).never()).bar();
- *
* verify(mock, timeout(200).atLeastOnce()).baz();
*
*
diff --git a/src/test/java/org/mockito/MockitoEnvTest.java b/src/test/java/org/mockito/MockitoEnvTest.java
new file mode 100644
index 0000000000..1a162e9390
--- /dev/null
+++ b/src/test/java/org/mockito/MockitoEnvTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+
+import org.junit.Assume;
+import org.junit.Test;
+import org.mockito.internal.configuration.plugins.DefaultMockitoPlugins;
+import org.mockito.internal.configuration.plugins.Plugins;
+import org.mockito.plugins.MemberAccessor;
+import org.mockito.plugins.MockMaker;
+
+public class MockitoEnvTest {
+ @Test
+ public void uses_default_mock_maker_from_env() {
+ final String mockMaker = System.getenv("MOCK_MAKER");
+ Assume.assumeThat(mockMaker, not(nullValue()));
+ Assume.assumeThat(mockMaker, endsWith("default"));
+
+ assertThat(DefaultMockitoPlugins.getDefaultPluginClass(MockMaker.class.getName()))
+ .isEqualTo(Plugins.getMockMaker().getClass().getName());
+ }
+
+ @Test
+ public void uses_mock_maker_from_env() {
+ final String mockMaker = System.getenv("MOCK_MAKER");
+ Assume.assumeThat(mockMaker, not(nullValue()));
+ Assume.assumeThat(mockMaker, not(endsWith("default")));
+
+ assertThat(DefaultMockitoPlugins.getDefaultPluginClass(mockMaker))
+ .isEqualTo(Plugins.getMockMaker().getClass().getName());
+ }
+
+ @Test
+ public void uses_default_member_accessor_from_env() {
+ final String memberAccessor = System.getenv("MEMBER_ACCESSOR");
+ Assume.assumeThat(memberAccessor, not(nullValue()));
+ Assume.assumeThat(memberAccessor, endsWith("default"));
+
+ assertThat(DefaultMockitoPlugins.getDefaultPluginClass(MemberAccessor.class.getName()))
+ .isEqualTo(Plugins.getMemberAccessor().getClass().getName());
+ }
+
+ @Test
+ public void uses_member_accessor_from_env() {
+ final String memberAccessor = System.getenv("MEMBER_ACCESSOR");
+ Assume.assumeThat(memberAccessor, not(nullValue()));
+ Assume.assumeThat(memberAccessor, not(endsWith("default")));
+
+ assertThat(DefaultMockitoPlugins.getDefaultPluginClass(memberAccessor))
+ .isEqualTo(Plugins.getMemberAccessor().getClass().getName());
+ }
+}
diff --git a/src/test/java/org/mockito/MockitoTest.java b/src/test/java/org/mockito/MockitoTest.java
index 8455011bb2..1671280d0c 100644
--- a/src/test/java/org/mockito/MockitoTest.java
+++ b/src/test/java/org/mockito/MockitoTest.java
@@ -4,18 +4,25 @@
*/
package org.mockito;
+import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.not;
import static org.mockito.Mockito.times;
import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress;
import java.util.List;
+import org.junit.Assume;
import org.junit.Test;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.exceptions.misusing.NotAMockException;
import org.mockito.exceptions.misusing.NullInsteadOfMockException;
+import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.internal.creation.MockSettingsImpl;
+import org.mockito.listeners.InvocationListener;
+import org.mockito.plugins.InlineMockMaker;
@SuppressWarnings("unchecked")
public class MockitoTest {
@@ -98,6 +105,8 @@ public void shouldValidateMockWhenCreatingInOrderObject() {
@SuppressWarnings({"CheckReturnValue", "MockitoUsage"})
@Test
public void shouldGiveExplanationOnStaticMockingWithoutInlineMockMaker() {
+ Assume.assumeThat(Plugins.getMockMaker(), not(instanceOf(InlineMockMaker.class)));
+
assertThatThrownBy(
() -> {
Mockito.mockStatic(Object.class);
@@ -113,6 +122,8 @@ public void shouldGiveExplanationOnStaticMockingWithoutInlineMockMaker() {
@SuppressWarnings({"CheckReturnValue", "MockitoUsage"})
@Test
public void shouldGiveExplanationOnConstructionMockingWithoutInlineMockMaker() {
+ Assume.assumeThat(Plugins.getMockMaker(), not(instanceOf(InlineMockMaker.class)));
+
assertThatThrownBy(
() -> {
Mockito.mockConstruction(Object.class);
@@ -125,6 +136,20 @@ public void shouldGiveExplanationOnConstructionMockingWithoutInlineMockMaker() {
"Note that Mockito's inline mock maker is not supported on Android.");
}
+ @SuppressWarnings({"CheckReturnValue", "MockitoUsage"})
+ @Test
+ public void shouldGiveExplanationOnConstructionMockingWithInlineMockMaker() {
+ Assume.assumeThat(Plugins.getMockMaker(), instanceOf(InlineMockMaker.class));
+
+ assertThatThrownBy(
+ () -> {
+ Mockito.mockConstruction(Object.class);
+ })
+ .isInstanceOf(MockitoException.class)
+ .hasMessageContainingAll(
+ "It is not possible to mock construction of the Object class to avoid inference with default object constructor chains");
+ }
+
@Test
public void shouldStartingMockSettingsContainDefaultBehavior() {
// given
@@ -133,4 +158,86 @@ public void shouldStartingMockSettingsContainDefaultBehavior() {
// when / then
assertThat(settings.getDefaultAnswer()).isEqualTo(Mockito.RETURNS_DEFAULTS);
}
+
+ @Test
+ @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"})
+ public void automaticallyDetectsClassToMock() {
+ List mock = Mockito.mock();
+ Mockito.when(mock.size()).thenReturn(42);
+ assertThat(mock.size()).isEqualTo(42);
+ }
+
+ @Test
+ @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"})
+ public void newMockMethod_shouldNotBeCalledWithNullParameters() {
+ assertThatThrownBy(
+ () -> {
+ Mockito.mock((Object[]) null);
+ })
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageStartingWith("Please don't pass any values here");
+ }
+
+ @Test
+ public void reifiedMockMethodWithNameSetsTheExpectedName() {
+ List mock = Mockito.mock("My super cool new mock");
+ assertThat(mock).hasToString("My super cool new mock");
+ }
+
+ @Test
+ public void reifiedMockMethodWithDefaultAnswerSetsTheDefaultAnswer() {
+ abstract class Something {
+ abstract Something somethingElse();
+ }
+
+ Something something = Mockito.mock(Answers.RETURNS_SELF);
+
+ assertThat(something.somethingElse()).isSameAs(something);
+ }
+
+ @Test
+ public void reifiedMockMethodWithSettingsAppliesTheSettings() {
+
+ InvocationListener invocationListener = Mockito.mock(InvocationListener.class);
+
+ List mock =
+ Mockito.mock(
+ Mockito.withSettings()
+ .name("my name here")
+ .invocationListeners(invocationListener));
+
+ assertThat(mock).hasToString("my name here");
+ Mockito.verify(invocationListener).reportInvocation(ArgumentMatchers.any());
+ }
+
+ @Test
+ @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"})
+ public void newMockMethod_shouldNotBeCalledWithParameters() {
+ assertThatThrownBy(
+ () -> {
+ Mockito.mock(asList("1", "2"), asList("3", "4"));
+ })
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageStartingWith("Please don't pass any values here");
+ }
+
+ @Test
+ @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"})
+ public void automaticallyDetectsClassToSpy() {
+ List mock = Mockito.spy();
+ Mockito.when(mock.size()).thenReturn(42);
+ assertThat(mock.size()).isEqualTo(42);
+ assertThat(mock.get(0)).isNull();
+ }
+
+ @Test
+ @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"})
+ public void newSpyMethod_shouldNotBeCalledWithParameters() {
+ assertThatThrownBy(
+ () -> {
+ Mockito.spy(asList("1", "2"), asList("3", "4"));
+ })
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageStartingWith("Please don't pass any values here");
+ }
}
diff --git a/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java b/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java
index 44afe0b6a9..61fc8e8ed1 100644
--- a/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java
+++ b/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java
@@ -7,9 +7,9 @@
import static org.junit.Assert.*;
import static org.mockito.internal.configuration.plugins.DefaultMockitoPlugins.INLINE_ALIAS;
import static org.mockito.internal.configuration.plugins.DefaultMockitoPlugins.PROXY_ALIAS;
+import static org.mockito.internal.configuration.plugins.DefaultMockitoPlugins.SUBCLASS_ALIAS;
import org.junit.Test;
-import org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker;
import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker;
import org.mockito.internal.util.ConsoleMockitoLogger;
import org.mockito.plugins.InstantiatorProvider2;
@@ -25,13 +25,17 @@ public class DefaultMockitoPluginsTest extends TestBase {
public void provides_plugins() throws Exception {
assertEquals(
"org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker",
- plugins.getDefaultPluginClass(INLINE_ALIAS));
+ DefaultMockitoPlugins.getDefaultPluginClass(INLINE_ALIAS));
assertEquals(InlineByteBuddyMockMaker.class, plugins.getInlineMockMaker().getClass());
assertEquals(
"org.mockito.internal.creation.proxy.ProxyMockMaker",
- plugins.getDefaultPluginClass(PROXY_ALIAS));
+ DefaultMockitoPlugins.getDefaultPluginClass(PROXY_ALIAS));
assertEquals(
- ByteBuddyMockMaker.class, plugins.getDefaultPlugin(MockMaker.class).getClass());
+ "org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker",
+ DefaultMockitoPlugins.getDefaultPluginClass(SUBCLASS_ALIAS));
+ assertEquals(
+ InlineByteBuddyMockMaker.class,
+ plugins.getDefaultPlugin(MockMaker.class).getClass());
assertNotNull(plugins.getDefaultPlugin(InstantiatorProvider2.class));
assertEquals(
ConsoleMockitoLogger.class,
diff --git a/src/test/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMakerTest.java b/src/test/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMakerTest.java
index 3d42f3a377..89aa415a96 100644
--- a/src/test/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMakerTest.java
+++ b/src/test/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMakerTest.java
@@ -80,6 +80,22 @@ public void should_create_mock_from_final_spy() throws Exception {
});
}
+ @Test
+ public void should_create_mock_from_accessible_inner_spy() throws Exception {
+ MockCreationSettings settings = settingsFor(Outer.Inner.class);
+ Optional proxy =
+ mockMaker.createSpy(
+ settings,
+ new MockHandlerImpl<>(settings),
+ new Outer.Inner(new Object(), new Object()));
+ assertThat(proxy)
+ .hasValueSatisfying(
+ spy -> {
+ assertThat(spy.p1).isNotNull();
+ assertThat(spy.p2).isNotNull();
+ });
+ }
+
@Test
public void should_create_mock_from_non_constructable_class() throws Exception {
MockCreationSettings settings =
@@ -646,4 +662,23 @@ void internalThrowException(int test) throws IOException {
}
}
}
+
+ static class Outer {
+
+ final Object p1;
+
+ private Outer(final Object p1) {
+ this.p1 = p1;
+ }
+
+ private static class Inner extends Outer {
+
+ final Object p2;
+
+ Inner(final Object p1, final Object p2) {
+ super(p1);
+ this.p2 = p2;
+ }
+ }
+ }
}
diff --git a/src/test/java/org/mockito/internal/debugging/LoggingListenerTest.java b/src/test/java/org/mockito/internal/debugging/LoggingListenerTest.java
index 54c5bec09d..a81aa98d2f 100644
--- a/src/test/java/org/mockito/internal/debugging/LoggingListenerTest.java
+++ b/src/test/java/org/mockito/internal/debugging/LoggingListenerTest.java
@@ -97,7 +97,7 @@ public void informs_about_various_kinds_of_stubs() {
+ "[Mockito]\n"
+ "[Mockito] 1. at com.FooTest:30\n"
+ "[Mockito]\n"
- + "[Mockito] Unstubbed method invocations (perhaps missing stubbing in the test?):\n"
+ + "[Mockito] Un-stubbed method invocations (perhaps missing stubbing in the test?):\n"
+ "[Mockito]\n"
+ "[Mockito] 1. at com.Foo:96",
listener.getStubbingInfo());
@@ -127,7 +127,7 @@ public void informs_about_unstubbed() {
assertEquals(
"[Mockito] Additional stubbing information (see javadoc for StubbingInfo class):\n"
+ "[Mockito]\n"
- + "[Mockito] Unstubbed method invocations (perhaps missing stubbing in the test?):\n"
+ + "[Mockito] Un-stubbed method invocations (perhaps missing stubbing in the test?):\n"
+ "[Mockito]\n"
+ "[Mockito] 1. com.Foo:20",
listener.getStubbingInfo());
diff --git a/src/test/java/org/mockito/internal/invocation/InvocationBuilder.java b/src/test/java/org/mockito/internal/invocation/InvocationBuilder.java
index 9900db9d98..d5cf3e5577 100644
--- a/src/test/java/org/mockito/internal/invocation/InvocationBuilder.java
+++ b/src/test/java/org/mockito/internal/invocation/InvocationBuilder.java
@@ -13,7 +13,7 @@
import java.util.List;
import org.mockito.Mockito;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.debugging.LocationFactory;
import org.mockito.internal.invocation.mockref.MockReference;
import org.mockito.internal.invocation.mockref.MockStrongReference;
import org.mockito.invocation.Invocation;
@@ -72,7 +72,7 @@ public Invocation toInvocation() {
new SerializableMethod(method),
args,
NO_OP,
- location == null ? new LocationImpl() : location,
+ location == null ? LocationFactory.create() : location,
1);
if (verified) {
i.markVerified();
diff --git a/src/test/java/org/mockito/internal/invocation/InvocationMatcherTest.java b/src/test/java/org/mockito/internal/invocation/InvocationMatcherTest.java
index 0b3d09d5f1..a217d4d8d3 100644
--- a/src/test/java/org/mockito/internal/invocation/InvocationMatcherTest.java
+++ b/src/test/java/org/mockito/internal/invocation/InvocationMatcherTest.java
@@ -136,7 +136,7 @@ public void should_be_similar_if_is_overloaded_but_used_with_different_arg() thr
public void should_capture_arguments_from_invocation() throws Exception {
// given
Invocation invocation = new InvocationBuilder().args("1", 100).toInvocation();
- CapturingMatcher capturingMatcher = new CapturingMatcher();
+ CapturingMatcher capturingMatcher = new CapturingMatcher(List.class);
InvocationMatcher invocationMatcher =
new InvocationMatcher(invocation, (List) asList(new Equals("1"), capturingMatcher));
@@ -149,11 +149,11 @@ public void should_capture_arguments_from_invocation() throws Exception {
}
@Test
- public void should_match_varargs_using_any_varargs() throws Exception {
+ public void should_match_varargs_using_any_varargs() {
// given
mock.varargs("1", "2");
Invocation invocation = getLastInvocation();
- InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List) asList(ANY));
+ InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, asList(ANY, ANY));
// when
boolean match = invocationMatcher.matches(invocation);
@@ -163,11 +163,11 @@ public void should_match_varargs_using_any_varargs() throws Exception {
}
@Test
- public void should_capture_varargs_as_vararg() throws Exception {
+ public void should_capture_varargs_as_vararg() {
// given
mock.mixedVarargs(1, "a", "b");
Invocation invocation = getLastInvocation();
- CapturingMatcher m = new CapturingMatcher();
+ CapturingMatcher m = new CapturingMatcher(String[].class);
InvocationMatcher invocationMatcher =
new InvocationMatcher(invocation, Arrays.asList(new Equals(1), m));
@@ -175,11 +175,11 @@ public void should_capture_varargs_as_vararg() throws Exception {
invocationMatcher.captureArgumentsFrom(invocation);
// then
- Assertions.assertThat(m.getAllValues()).containsExactly("a", "b");
+ Assertions.assertThat(m.getAllValues()).containsExactly(new String[] {"a", "b"});
}
@Test // like using several time the captor in the vararg
- public void should_capture_arguments_when_args_count_does_NOT_match() throws Exception {
+ public void should_capture_arguments_when_args_count_does_NOT_match() {
// given
mock.varargs();
Invocation invocation = getLastInvocation();
diff --git a/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java b/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java
index 285bfecc66..21c94b1525 100644
--- a/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java
+++ b/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java
@@ -25,7 +25,6 @@
import org.mockito.internal.matchers.Any;
import org.mockito.internal.matchers.Equals;
import org.mockito.internal.matchers.InstanceOf;
-import org.mockito.internal.matchers.VarargMatcher;
import org.mockito.invocation.Invocation;
import org.mockitousage.IMethods;
import org.mockitoutil.TestBase;
@@ -35,7 +34,7 @@ public class MatcherApplicationStrategyTest extends TestBase {
@Mock IMethods mock;
private Invocation invocation;
- private List matchers;
+ private List extends ArgumentMatcher>> matchers;
private RecordingAction recordAction;
@@ -123,7 +122,7 @@ public void shouldKnowWhenVarargsMatch() {
public void shouldAllowAnyMatchEntireVararg() {
// given
invocation = varargs("1", "2");
- matchers = asList(ANY);
+ matchers = asList(ANY, ANY);
// when
boolean match =
@@ -150,10 +149,10 @@ public void shouldNotAllowAnyWithMixedVarargs() {
}
@Test
- public void shouldAllowanyWithMixedVarargs() {
+ public void shouldAllowAnyWithMixedVarargs() {
// given
invocation = mixedVarargs(1, "1", "2");
- matchers = asList(new Equals(1), ANY);
+ matchers = asList(new Equals(1), ANY, ANY);
// when
boolean match =
@@ -185,7 +184,7 @@ public void shouldAnyDealWithDifferentSizeOfArgs() {
public void shouldMatchAnyEvenIfOneOfTheArgsIsNull() {
// given
invocation = mixedVarargs(null, null, "2");
- matchers = asList(new Equals(null), ANY);
+ matchers = asList(new Equals(null), ANY, ANY);
// when
getMatcherApplicationStrategyFor(invocation, matchers)
@@ -199,7 +198,7 @@ public void shouldMatchAnyEvenIfOneOfTheArgsIsNull() {
public void shouldMatchAnyEvenIfMatcherIsDecorated() {
// given
invocation = varargs("1", "2");
- matchers = asList(ANY);
+ matchers = asList(ANY, ANY);
// when
getMatcherApplicationStrategyFor(invocation, matchers)
@@ -213,8 +212,9 @@ public void shouldMatchAnyEvenIfMatcherIsDecorated() {
public void shouldMatchAnyEvenIfMatcherIsWrappedInHamcrestMatcher() {
// given
invocation = varargs("1", "2");
- HamcrestArgumentMatcher argumentMatcher = new HamcrestArgumentMatcher(new IntMatcher());
- matchers = asList(argumentMatcher);
+ HamcrestArgumentMatcher argumentMatcher =
+ new HamcrestArgumentMatcher<>(new IntMatcher());
+ matchers = asList(argumentMatcher, argumentMatcher);
// when
getMatcherApplicationStrategyFor(invocation, matchers)
@@ -224,7 +224,30 @@ public void shouldMatchAnyEvenIfMatcherIsWrappedInHamcrestMatcher() {
recordAction.assertContainsExactly(argumentMatcher, argumentMatcher);
}
- class IntMatcher extends BaseMatcher implements VarargMatcher {
+ @Test
+ public void shouldMatchAnyThatMatchesRawVarArgType() {
+ // given
+ invocation = varargs("1", "2");
+ InstanceOf any = new InstanceOf(String[].class, "");
+ matchers = asList(any);
+
+ // when
+ getMatcherApplicationStrategyFor(invocation, matchers)
+ .forEachMatcherAndArgument(recordAction);
+
+ // then
+ recordAction.assertContainsExactly(any);
+ }
+
+ private static class IntMatcher extends BaseMatcher {
+ public boolean matches(Object o) {
+ return true;
+ }
+
+ public void describeTo(Description description) {}
+ }
+
+ private static class IntArrayMatcher extends BaseMatcher {
public boolean matches(Object o) {
return true;
}
@@ -242,8 +265,8 @@ private Invocation varargs(String... s) {
return getLastInvocation();
}
- private class RecordingAction implements ArgumentMatcherAction {
- private List> matchers = new ArrayList>();
+ private static class RecordingAction implements ArgumentMatcherAction {
+ private final List> matchers = new ArrayList>();
@Override
public boolean apply(ArgumentMatcher> matcher, Object argument) {
diff --git a/src/test/java/org/mockito/internal/matchers/AndTest.java b/src/test/java/org/mockito/internal/matchers/AndTest.java
new file mode 100644
index 0000000000..3115576d9e
--- /dev/null
+++ b/src/test/java/org/mockito/internal/matchers/AndTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2022 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.matchers;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockitoutil.TestBase;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.BDDMockito.given;
+
+public class AndTest extends TestBase {
+
+ @Mock private ArgumentMatcher> m1;
+ @Mock private ArgumentMatcher> m2;
+ private And and;
+
+ @Before
+ public void setUp() throws Exception {
+ and = new And(m1, m2);
+ }
+
+ @Test
+ public void shouldReturnMatchingTypes() {
+ given(m1.type()).will(inv -> String.class);
+ given(m2.type()).will(inv -> String.class);
+
+ assertThat(and.type()).isEqualTo(String.class);
+ }
+
+ @Test
+ public void shouldDefaultMismatchingTypes() {
+ given(m1.type()).will(inv -> String.class);
+ given(m2.type()).will(inv -> Integer.class);
+
+ assertThat(and.type()).isEqualTo(Void.class);
+ }
+
+ @Test
+ public void shouldReturnLeftBaseType() {
+ given(m1.type()).will(inv -> BaseType.class);
+ given(m2.type()).will(inv -> SubType.class);
+
+ assertThat(and.type()).isEqualTo(BaseType.class);
+ }
+
+ @Test
+ public void shouldReturnRightBaseType() {
+ given(m1.type()).will(inv -> SubType.class);
+ given(m2.type()).will(inv -> BaseType.class);
+
+ assertThat(and.type()).isEqualTo(BaseType.class);
+ }
+
+ private interface BaseType {}
+
+ private interface SubType extends BaseType {}
+}
diff --git a/src/test/java/org/mockito/internal/matchers/CapturingMatcherTest.java b/src/test/java/org/mockito/internal/matchers/CapturingMatcherTest.java
index a4c6b59149..768d08d0b8 100644
--- a/src/test/java/org/mockito/internal/matchers/CapturingMatcherTest.java
+++ b/src/test/java/org/mockito/internal/matchers/CapturingMatcherTest.java
@@ -19,7 +19,7 @@ public class CapturingMatcherTest extends TestBase {
@Test
public void should_capture_arguments() throws Exception {
// given
- CapturingMatcher m = new CapturingMatcher();
+ CapturingMatcher m = new CapturingMatcher(String.class);
// when
m.captureFrom("foo");
@@ -32,7 +32,7 @@ public void should_capture_arguments() throws Exception {
@Test
public void should_know_last_captured_value() throws Exception {
// given
- CapturingMatcher m = new CapturingMatcher();
+ CapturingMatcher m = new CapturingMatcher(String.class);
// when
m.captureFrom("foo");
@@ -45,7 +45,7 @@ public void should_know_last_captured_value() throws Exception {
@Test
public void should_scream_when_nothing_yet_captured() throws Exception {
// given
- CapturingMatcher m = new CapturingMatcher();
+ CapturingMatcher m = new CapturingMatcher(String.class);
try {
// when
@@ -59,7 +59,7 @@ public void should_scream_when_nothing_yet_captured() throws Exception {
@Test
public void should_not_fail_when_used_in_concurrent_tests() throws Exception {
// given
- final CapturingMatcher m = new CapturingMatcher();
+ final CapturingMatcher m = new CapturingMatcher(String.class);
// when
m.captureFrom("concurrent access");
diff --git a/src/test/java/org/mockito/internal/matchers/EqualsTest.java b/src/test/java/org/mockito/internal/matchers/EqualsTest.java
index ba5c578b57..e56de2a9c2 100644
--- a/src/test/java/org/mockito/internal/matchers/EqualsTest.java
+++ b/src/test/java/org/mockito/internal/matchers/EqualsTest.java
@@ -4,9 +4,11 @@
*/
package org.mockito.internal.matchers;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;
import org.junit.Test;
+import org.mockito.ArgumentMatcher;
import org.mockitoutil.TestBase;
public class EqualsTest extends TestBase {
@@ -102,4 +104,15 @@ public void shouldMatchTypesSafelyWhenGivenIsNull() throws Exception {
// then
assertFalse(equals.typeMatches(null));
}
+
+ @Test
+ public void shouldInferType() {
+ assertThat(new Equals("String").type()).isEqualTo(String.class);
+ }
+
+ @Test
+ public void shouldDefaultTypeOnNull() {
+ assertThat(new Equals(null).type())
+ .isEqualTo(((ArgumentMatcher) argument -> false).type());
+ }
}
diff --git a/src/test/java/org/mockito/internal/matchers/InstanceOfTest.java b/src/test/java/org/mockito/internal/matchers/InstanceOfTest.java
index 8addef8ca0..7740d0b77d 100644
--- a/src/test/java/org/mockito/internal/matchers/InstanceOfTest.java
+++ b/src/test/java/org/mockito/internal/matchers/InstanceOfTest.java
@@ -59,9 +59,8 @@ public void should_check_for_primitive_wrapper_types() {
@Test
public void can_be_vararg_aware() {
- assertThat(new InstanceOf.VarArgAware(Number[].class)).isInstanceOf(VarargMatcher.class);
- assertThat(new InstanceOf.VarArgAware(Number[].class).matches(new Integer[0])).isTrue();
- assertThat(new InstanceOf.VarArgAware(Number[].class).matches(new Number[0])).isTrue();
- assertThat(new InstanceOf.VarArgAware(Number[].class).matches(new Object[0])).isFalse();
+ assertThat(new InstanceOf(Number[].class).matches(new Integer[0])).isTrue();
+ assertThat(new InstanceOf(Number[].class).matches(new Number[0])).isTrue();
+ assertThat(new InstanceOf(Number[].class).matches(new Object[0])).isFalse();
}
}
diff --git a/src/test/java/org/mockito/internal/matchers/OrTest.java b/src/test/java/org/mockito/internal/matchers/OrTest.java
new file mode 100644
index 0000000000..6738d73507
--- /dev/null
+++ b/src/test/java/org/mockito/internal/matchers/OrTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2022 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.matchers;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockitoutil.TestBase;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.BDDMockito.given;
+
+public class OrTest extends TestBase {
+
+ @Mock private ArgumentMatcher> m1;
+ @Mock private ArgumentMatcher> m2;
+ private Or or;
+
+ @Before
+ public void setUp() throws Exception {
+ or = new Or(m1, m2);
+ }
+
+ @Test
+ public void shouldReturnMatchingTypes() {
+ given(m1.type()).will(inv -> String.class);
+ given(m2.type()).will(inv -> String.class);
+
+ assertThat(or.type()).isEqualTo(String.class);
+ }
+
+ @Test
+ public void shouldDefaultMismatchingTypes() {
+ given(m1.type()).will(inv -> String.class);
+ given(m2.type()).will(inv -> Integer.class);
+
+ assertThat(or.type()).isEqualTo(Void.class);
+ }
+
+ @Test
+ public void shouldReturnLeftBaseType() {
+ given(m1.type()).will(inv -> BaseType.class);
+ given(m2.type()).will(inv -> SubType.class);
+
+ assertThat(or.type()).isEqualTo(BaseType.class);
+ }
+
+ @Test
+ public void shouldReturnRightBaseType() {
+ given(m1.type()).will(inv -> SubType.class);
+ given(m2.type()).will(inv -> BaseType.class);
+
+ assertThat(or.type()).isEqualTo(BaseType.class);
+ }
+
+ private interface BaseType {}
+
+ private interface SubType extends BaseType {}
+}
diff --git a/src/test/java/org/mockito/internal/matchers/SameTest.java b/src/test/java/org/mockito/internal/matchers/SameTest.java
new file mode 100644
index 0000000000..d5c6a1f9c8
--- /dev/null
+++ b/src/test/java/org/mockito/internal/matchers/SameTest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.matchers;
+
+import org.mockitoutil.TestBase;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SameTest extends TestBase {
+
+ @Test
+ public void shouldInferType() {
+ assertThat(new Same("String").type()).isEqualTo(String.class);
+ }
+
+ @Test
+ public void shouldDefaultTypeOnNull() {
+ assertThat(new Same(null).type())
+ .isEqualTo(((ArgumentMatcher) argument -> false).type());
+ }
+}
diff --git a/src/test/java/org/mockito/internal/runners/DefaultInternalRunnerTest.java b/src/test/java/org/mockito/internal/runners/DefaultInternalRunnerTest.java
index c8912ec223..0e4e40076e 100644
--- a/src/test/java/org/mockito/internal/runners/DefaultInternalRunnerTest.java
+++ b/src/test/java/org/mockito/internal/runners/DefaultInternalRunnerTest.java
@@ -4,11 +4,14 @@
*/
package org.mockito.internal.runners;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
+import org.junit.Assume;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
@@ -18,9 +21,11 @@
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.Statement;
import org.mockito.Mock;
+import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.internal.junit.MockitoTestListener;
import org.mockito.internal.junit.TestFinishedEvent;
import org.mockito.internal.util.Supplier;
+import org.mockito.plugins.InlineMockMaker;
public class DefaultInternalRunnerTest {
@@ -42,6 +47,9 @@ public void does_not_fail_when_tests_succeeds() throws Exception {
@Test
public void does_not_fail_second_test_when_first_test_fail() throws Exception {
+ // The TestFailOnInitialization is initialized properly by inline mock maker
+ Assume.assumeThat(Plugins.getMockMaker(), not(instanceOf(InlineMockMaker.class)));
+
new DefaultInternalRunner(TestFailOnInitialization.class, supplier)
.run(newNotifier(runListener));
diff --git a/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNullsTest.java b/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNullsTest.java
index 984e07da2a..267c6d6966 100644
--- a/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNullsTest.java
+++ b/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNullsTest.java
@@ -20,7 +20,7 @@
import org.assertj.core.api.ThrowableAssert;
import org.junit.Test;
import org.mockito.exceptions.verification.SmartNullPointerException;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.debugging.LocationFactory;
import org.mockito.internal.invocation.InterceptedInvocation;
import org.mockito.internal.invocation.SerializableMethod;
import org.mockito.internal.invocation.mockref.MockStrongReference;
@@ -146,7 +146,7 @@ private static InterceptedInvocation invocationMethodWithArgs(final T obj)
GenericFooBar.class.getMethod("methodWithArgs", int.class, Object.class)),
new Object[] {1, obj},
InterceptedInvocation.NO_OP,
- new LocationImpl(),
+ LocationFactory.create(),
1);
}
@@ -269,7 +269,7 @@ private static InterceptedInvocation invocationMethodWithVarArgs(final T[] o
"methodWithVarArgs", int.class, Object[].class)),
new Object[] {1, obj},
InterceptedInvocation.NO_OP,
- new LocationImpl(),
+ LocationFactory.create(),
1);
}
diff --git a/src/test/java/org/mockito/internal/util/MockCreationValidatorTest.java b/src/test/java/org/mockito/internal/util/MockCreationValidatorTest.java
index 7efffcf6a2..7491a3f070 100644
--- a/src/test/java/org/mockito/internal/util/MockCreationValidatorTest.java
+++ b/src/test/java/org/mockito/internal/util/MockCreationValidatorTest.java
@@ -66,7 +66,7 @@ public void should_validation_be_safe_when_nulls_passed() {
@Test
public void should_fail_when_type_not_mockable() {
try {
- validator.validateType(long.class);
+ validator.validateType(long.class, null);
} catch (MockitoException ex) {
assertThat(ex.getMessage()).contains("primitive");
}
diff --git a/src/test/java/org/mockito/internal/util/MockUtilTest.java b/src/test/java/org/mockito/internal/util/MockUtilTest.java
index 50cadb4c0c..834178cdec 100644
--- a/src/test/java/org/mockito/internal/util/MockUtilTest.java
+++ b/src/test/java/org/mockito/internal/util/MockUtilTest.java
@@ -100,12 +100,12 @@ interface SomeInterface {}
@Test
public void should_know_if_type_is_mockable() throws Exception {
- Assertions.assertThat(MockUtil.typeMockabilityOf(FinalClass.class).mockable())
+ Assertions.assertThat(MockUtil.typeMockabilityOf(FinalClass.class, null).mockable())
.isEqualTo(Plugins.getMockMaker().isTypeMockable(FinalClass.class).mockable());
- assertFalse(MockUtil.typeMockabilityOf(int.class).mockable());
+ assertFalse(MockUtil.typeMockabilityOf(int.class, null).mockable());
- assertTrue(MockUtil.typeMockabilityOf(SomeClass.class).mockable());
- assertTrue(MockUtil.typeMockabilityOf(SomeInterface.class).mockable());
+ assertTrue(MockUtil.typeMockabilityOf(SomeClass.class, null).mockable());
+ assertTrue(MockUtil.typeMockabilityOf(SomeInterface.class, null).mockable());
}
}
diff --git a/src/test/java/org/mockito/internal/util/PlatformTest.java b/src/test/java/org/mockito/internal/util/PlatformTest.java
index d4c453386d..42f59a1bc6 100644
--- a/src/test/java/org/mockito/internal/util/PlatformTest.java
+++ b/src/test/java/org/mockito/internal/util/PlatformTest.java
@@ -63,34 +63,6 @@ public void should_warn_for_jvm() throws Exception {
.isEqualTo("");
}
- @Test
- public void should_parse_open_jdk_string_and_report_wether_below_or_nut_update_45() {
- // Given
- // Sources :
- // - https://www.oracle.com/java/technologies/javase/versioning-naming.html
- // - https://www.oracle.com/java/technologies/javase/jdk7-naming.html
- // - https://www.oracle.com/java/technologies/javase/jdk8-naming.html
- // -
- // https://stackoverflow.com/questions/35844985/how-do-we-get-sr-and-fp-of-ibm-jre-using-java
- // -
- // https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.80.doc/user/build_number.html
- Map versions = new HashMap<>();
- versions.put("1.8.0_92-b14", false);
- versions.put("1.8.0-b24", true);
- versions.put("1.8.0_5", true);
- versions.put("1.8.0b5_u44", true);
- versions.put("1.8.0b5_u92", false);
- versions.put("1.7.0_4", false);
- versions.put("1.4.0_03-b04", false);
- versions.put("1.4.0_03-ea-b01", false);
- versions.put("pxi3270_27sr4-20160303_03 (SR4)", false);
- versions.put("pwi3260sr11-20120412_01 (SR11)", false);
- versions.put("pwa6480sr1fp10-20150711_01 (SR1 FP10)", false);
- versions.put("null", false);
-
- assertPlatformParsesCorrectlyVariousVersionScheme(versions);
- }
-
@Test
public void should_parse_open_jdk9_string() {
// The tested method targets Java 8 but should be able to parse other Java version numbers
@@ -140,9 +112,7 @@ public void should_parse_open_jdk9_string() {
private void assertPlatformParsesCorrectlyVariousVersionScheme(Map versions) {
for (Map.Entry version : versions.entrySet()) {
- assertThat(Platform.isJava8BelowUpdate45(version.getKey()))
- .describedAs(version.getKey())
- .isEqualTo(version.getValue());
+ assertThat(version.getValue()).describedAs(version.getKey()).isEqualTo(false);
}
}
}
diff --git a/src/test/java/org/mockito/internal/util/reflection/ParameterizedConstructorInstantiatorTest.java b/src/test/java/org/mockito/internal/util/reflection/ParameterizedConstructorInstantiatorTest.java
index 9e63e8df69..45ff5ea86f 100644
--- a/src/test/java/org/mockito/internal/util/reflection/ParameterizedConstructorInstantiatorTest.java
+++ b/src/test/java/org/mockito/internal/util/reflection/ParameterizedConstructorInstantiatorTest.java
@@ -73,7 +73,7 @@ public void should_be_created_with_an_argument_resolver() throws Exception {
public void should_instantiate_type_if_resolver_provide_matching_types() throws Exception {
Observer observer = mock(Observer.class);
Map map = mock(Map.class);
- given(resolver.resolveTypeInstances(ArgumentMatchers.[]>any()))
+ given(resolver.resolveTypeInstances(ArgumentMatchers.any(Class[].class)))
.willReturn(new Object[] {observer, map});
new ParameterizedConstructorInstantiator(this, field("withMultipleConstructor"), resolver)
@@ -120,7 +120,7 @@ this, field("withThrowingConstructor"), resolver)
@Test
public void should_instantiate_type_with_vararg_constructor() throws Exception {
Observer[] vararg = new Observer[] {};
- given(resolver.resolveTypeInstances(ArgumentMatchers.[]>any()))
+ given(resolver.resolveTypeInstances(ArgumentMatchers.any(Class[].class)))
.willReturn(new Object[] {"", vararg});
new ParameterizedConstructorInstantiator(this, field("withVarargConstructor"), resolver)
diff --git a/src/test/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingToolTest.java b/src/test/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingToolTest.java
index 5f55faf3b2..27c986f43f 100644
--- a/src/test/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingToolTest.java
+++ b/src/test/java/org/mockito/internal/verification/argumentmatching/ArgumentMatchingToolTest.java
@@ -17,7 +17,7 @@
import org.mockito.internal.matchers.Equals;
import org.mockitoutil.TestBase;
-@SuppressWarnings({"unchecked", "serial"})
+@SuppressWarnings({"rawtypes", "unchecked", "serial"})
public class ArgumentMatchingToolTest extends TestBase {
@Test
@@ -97,7 +97,6 @@ public void shouldWorkFineWhenGivenArgIsNull() {
}
@Test
- @SuppressWarnings("rawtypes")
public void shouldUseMatchersSafely() {
// This matcher is evil cause typeMatches(Object) returns true for every passed type but
// matches(T)
@@ -137,4 +136,49 @@ public Object getWanted() {
// then
assertEquals(0, suspicious.length);
}
+
+ @Test
+ public void shouldNotFindNonMatchingIndexesWhenEveryArgsMatch() {
+ String arg1Value = "arg1";
+ Integer arg2Value = 2222;
+
+ Equals arg1 = new Equals(arg1Value);
+ Equals arg2 = new Equals(arg2Value);
+
+ List indexes =
+ ArgumentMatchingTool.getNotMatchingArgsIndexes(
+ Arrays.asList(arg1, arg2), new Object[] {arg1Value, arg2Value});
+
+ assertEquals(Arrays.asList(), indexes);
+ }
+
+ @Test
+ public void shouldFindNonMatchingIndexesWhenSingleArgDoesNotMatch() {
+ String arg1Value = "arg1";
+ Integer arg2Value = 2222;
+
+ Equals arg1 = new Equals(arg1Value);
+ Equals arg2 = new Equals(1111);
+
+ List indexes =
+ ArgumentMatchingTool.getNotMatchingArgsIndexes(
+ Arrays.asList(arg1, arg2), new Object[] {arg1Value, arg2Value});
+
+ assertEquals(Arrays.asList(1), indexes);
+ }
+
+ @Test
+ public void shouldFindNonMatchingIndexesWhenMultiArgsDoNotMatch() {
+ String arg1Value = "arg1";
+ Integer arg2Value = 2222;
+
+ Equals arg1 = new Equals("differs");
+ Equals arg2 = new Equals(1111);
+
+ List indexes =
+ ArgumentMatchingTool.getNotMatchingArgsIndexes(
+ Arrays.asList(arg1, arg2), new Object[] {arg1Value, arg2Value});
+
+ assertEquals(Arrays.asList(0, 1), indexes);
+ }
}
diff --git a/src/test/java/org/mockito/internal/verification/checkers/MissingInvocationCheckerTest.java b/src/test/java/org/mockito/internal/verification/checkers/MissingInvocationCheckerTest.java
index 911a9a59ac..cd7da33ca2 100644
--- a/src/test/java/org/mockito/internal/verification/checkers/MissingInvocationCheckerTest.java
+++ b/src/test/java/org/mockito/internal/verification/checkers/MissingInvocationCheckerTest.java
@@ -95,6 +95,76 @@ public void shouldReportUsingInvocationDescription() {
"mock.intArgumentMethod(MyCoolPrint(1111));");
}
+ @Test
+ public void shouldSpecifyPosition0WhenWantedInvocationDiffersFromActual() {
+ wanted = buildMultiArgsMethod().args("arg1", 2222).toInvocationMatcher();
+ invocations = singletonList(buildMultiArgsMethod().args("differs", 2222).toInvocation());
+
+ assertThatThrownBy(
+ () -> {
+ MissingInvocationChecker.checkMissingInvocation(invocations, wanted);
+ })
+ .isInstanceOf(ArgumentsAreDifferent.class)
+ .hasMessageContainingAll(
+ "Argument(s) are different! Wanted:",
+ "mock.simpleMethod(\"arg1\", 2222);",
+ "Actual invocations have different arguments at position [0]:",
+ "mock.simpleMethod(\"differs\", 2222);");
+ }
+
+ @Test
+ public void shouldSpecifyPosition1WhenWantedInvocationDiffersFromActual() {
+ wanted = buildMultiArgsMethod().args("arg1", 2222).toInvocationMatcher();
+ invocations = singletonList(buildMultiArgsMethod().args("arg1", 1111).toInvocation());
+
+ assertThatThrownBy(
+ () -> {
+ MissingInvocationChecker.checkMissingInvocation(invocations, wanted);
+ })
+ .isInstanceOf(ArgumentsAreDifferent.class)
+ .hasMessageContainingAll(
+ "Argument(s) are different! Wanted:",
+ "mock.simpleMethod(\"arg1\", 2222);",
+ "Actual invocations have different arguments at position [1]:",
+ "mock.simpleMethod(\"arg1\", 1111);");
+ }
+
+ @Test
+ public void shouldSpecifyPosition0And1WhenWantedInvocationDiffersFromActual() {
+ wanted = buildMultiArgsMethod().args("arg1", 2222).toInvocationMatcher();
+ invocations = singletonList(buildMultiArgsMethod().args("differs", 1111).toInvocation());
+
+ assertThatThrownBy(
+ () -> {
+ MissingInvocationChecker.checkMissingInvocation(invocations, wanted);
+ })
+ .isInstanceOf(ArgumentsAreDifferent.class)
+ .hasMessageContainingAll(
+ "Argument(s) are different! Wanted:",
+ "mock.simpleMethod(\"arg1\", 2222);",
+ "Actual invocations have different arguments at positions [0, 1]:",
+ "mock.simpleMethod(\"differs\", 1111);");
+ }
+
+ @Test
+ public void shouldNotSpecifyPositionWhenWantedSingleArgInvocationSiffersFromActual() {
+ wanted = buildIntArgMethod(new CustomInvocationBuilder()).arg(2222).toInvocationMatcher();
+ invocations =
+ singletonList(
+ buildIntArgMethod(new CustomInvocationBuilder()).arg(1111).toInvocation());
+
+ assertThatThrownBy(
+ () -> {
+ MissingInvocationChecker.checkMissingInvocation(invocations, wanted);
+ })
+ .isInstanceOf(ArgumentsAreDifferent.class)
+ .hasMessageContainingAll(
+ "Argument(s) are different! Wanted:",
+ "mock.intArgumentMethod(MyCoolPrint(2222));",
+ "Actual invocations have different arguments:",
+ "mock.intArgumentMethod(MyCoolPrint(1111));");
+ }
+
private InvocationBuilder buildIntArgMethod(InvocationBuilder invocationBuilder) {
return invocationBuilder.mock(mock).method("intArgumentMethod").argTypes(int.class);
}
@@ -107,6 +177,13 @@ private InvocationBuilder buildDifferentMethod() {
return new InvocationBuilder().mock(mock).differentMethod();
}
+ private InvocationBuilder buildMultiArgsMethod() {
+ return new InvocationBuilder()
+ .mock(mock)
+ .method("simpleMethod")
+ .argTypes(String.class, Integer.class);
+ }
+
static class CustomInvocationBuilder extends InvocationBuilder {
@Override
protected Invocation createInvocation(
diff --git a/src/test/java/org/mockitointegration/ClassLoadabilityChecker.java b/src/test/java/org/mockitointegration/ClassLoadabilityChecker.java
new file mode 100644
index 0000000000..d19c51fb74
--- /dev/null
+++ b/src/test/java/org/mockitointegration/ClassLoadabilityChecker.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockitointegration;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Check that classes can be loaded and initialized on a provided classloader. Used
+ * for checking that Mockito has no dependency on libraries like JUnit.
+ *
+ * Some classes are excluded from this checking - namely, classes that fail due to
+ * the absence of Java classes. It's assumed that this is due to a specific optional
+ * dependency on APIs available in certain Java versions and so other elements of the
+ * test Matrix will check that those classes do not depend on JUnit or ByteBuddy. We
+ * exclude based on the failure of a ClassNotFoundException, or a NoClassDefFoundError
+ * caused by the failing to load of a failing parent class.
+ */
+public final class ClassLoadabilityChecker {
+ private static final boolean INITIALIZE_CLASSES = true;
+ private final Set excludedClasses = new HashSet<>();
+ private final ClassLoader classLoader;
+ private final String purpose;
+
+ public ClassLoadabilityChecker(ClassLoader classLoader, String purpose) {
+ this.classLoader = classLoader;
+ this.purpose = purpose;
+ }
+
+ public void checkLoadability(String className) {
+ try {
+ Class.forName(className, INITIALIZE_CLASSES, classLoader);
+ } catch (ClassNotFoundException | LinkageError e) {
+ if (isFailureExcluded(className, e)) {
+ return;
+ }
+ e.printStackTrace();
+ throw new AssertionError(
+ String.format("'%s' has some dependency to %s", className, purpose));
+ }
+ }
+
+ private boolean isFailureExcluded(String loadedClass, Throwable thrown) {
+ if (thrown == null) {
+ return false;
+ }
+ if (thrown instanceof ClassNotFoundException) {
+ ClassNotFoundException cnf = (ClassNotFoundException) thrown;
+ if (cnf.getMessage().startsWith("java.")) {
+ excludedClasses.add(loadedClass);
+ return true;
+ }
+ } else if (thrown instanceof NoClassDefFoundError) {
+ NoClassDefFoundError ncdf = (NoClassDefFoundError) thrown;
+ // if Foo fails due to depending on a Java class, Foo$Bar will fail with a NCDFE
+ int lastInnerClass = loadedClass.lastIndexOf('$');
+ if (lastInnerClass != -1) {
+ String parent = loadedClass.substring(0, lastInnerClass);
+ if (excludedClasses.contains(parent) && ncdf.getMessage().contains(parent)) {
+ excludedClasses.add(loadedClass);
+ return true;
+ }
+ }
+ }
+
+ return isFailureExcluded(loadedClass, thrown.getCause());
+ }
+}
diff --git a/src/test/java/org/mockitointegration/DeferMockMakersClassLoadingTest.java b/src/test/java/org/mockitointegration/DeferMockMakersClassLoadingTest.java
new file mode 100644
index 0000000000..63c0d761bd
--- /dev/null
+++ b/src/test/java/org/mockitointegration/DeferMockMakersClassLoadingTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockitointegration;
+
+import static org.mockito.Mockito.withSettings;
+import static org.mockitoutil.ClassLoaders.coverageTool;
+
+import java.lang.reflect.Method;
+
+import org.assertj.core.api.Assertions;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker;
+import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker;
+import org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker;
+import org.mockito.internal.creation.proxy.ProxyMockMaker;
+import org.mockito.invocation.MockHandler;
+import org.mockito.mock.MockCreationSettings;
+import org.mockito.plugins.MockMaker;
+import org.mockitoutil.ClassLoaders;
+
+public class DeferMockMakersClassLoadingTest {
+ private static final Object MY_MOCK = new Object();
+
+ @Test
+ public void mockito_should_not_load_mock_makers_it_does_not_need() throws Exception {
+ ClassLoader classLoader_without_mockMakers =
+ ClassLoaders.excludingClassLoader()
+ .withCodeSourceUrlOf(
+ Mockito.class,
+ Matcher.class,
+ CustomMockMaker.class,
+ Assertions.class)
+ .withCodeSourceUrlOf(coverageTool())
+ .without(
+ ByteBuddyMockMaker.class.getName(),
+ SubclassByteBuddyMockMaker.class.getName(),
+ InlineByteBuddyMockMaker.class.getName(),
+ ProxyMockMaker.class.getName())
+ .build();
+
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(classLoader_without_mockMakers);
+ try {
+ Class> self = classLoader_without_mockMakers.loadClass(getClass().getName());
+ Method createMock = self.getMethod("createMock");
+ createMock.invoke(null);
+ } finally {
+ Thread.currentThread().setContextClassLoader(contextClassLoader);
+ }
+ }
+
+ // Called by reflection from the test method
+ public static void createMock() {
+ Assertions.assertThat(
+ Mockito.mock(
+ Object.class,
+ withSettings().mockMaker(CustomMockMaker.class.getName())))
+ .isSameAs(MY_MOCK);
+ }
+
+ public static class CustomMockMaker implements MockMaker {
+ @Override
+ public T createMock(MockCreationSettings settings, MockHandler handler) {
+ return settings.getTypeToMock().cast(MY_MOCK);
+ }
+
+ @Override
+ public MockHandler getHandler(Object mock) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void resetMock(Object mock, MockHandler newHandler, MockCreationSettings settings) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public TypeMockability isTypeMockable(Class> type) {
+ return new TypeMockability() {
+ @Override
+ public boolean mockable() {
+ return type.equals(Object.class);
+ }
+
+ @Override
+ public String nonMockableReason() {
+ return mockable() ? "" : "type != Object.class";
+ }
+ };
+ }
+ }
+}
diff --git a/src/test/java/org/mockitointegration/NoByteCodeDependenciesTest.java b/src/test/java/org/mockitointegration/NoByteCodeDependenciesTest.java
index 3a2908dc1c..3db1395066 100644
--- a/src/test/java/org/mockitointegration/NoByteCodeDependenciesTest.java
+++ b/src/test/java/org/mockitointegration/NoByteCodeDependenciesTest.java
@@ -43,21 +43,11 @@ public void pure_mockito_should_not_depend_bytecode_libraries() throws Exception
pureMockitoAPIClasses.remove(
"org.mockito.internal.util.reflection.InstrumentationMemberAccessor");
+ ClassLoadabilityChecker checker =
+ new ClassLoadabilityChecker(
+ classLoader_without_bytecode_libraries, "ByteBuddy or Objenesis");
for (String pureMockitoAPIClass : pureMockitoAPIClasses) {
- checkDependency(classLoader_without_bytecode_libraries, pureMockitoAPIClass);
- }
- }
-
- private void checkDependency(ClassLoader classLoader, String pureMockitoAPIClass)
- throws ClassNotFoundException {
- try {
- Class.forName(pureMockitoAPIClass, true, classLoader);
- } catch (Throwable e) {
- e.printStackTrace();
- throw new AssertionError(
- String.format(
- "'%s' has some dependency to Byte Buddy or Objenesis",
- pureMockitoAPIClass));
+ checker.checkLoadability(pureMockitoAPIClass);
}
}
}
diff --git a/src/test/java/org/mockitointegration/NoJUnitDependenciesTest.java b/src/test/java/org/mockitointegration/NoJUnitDependenciesTest.java
index 7b156f0aa3..503d859617 100644
--- a/src/test/java/org/mockitointegration/NoJUnitDependenciesTest.java
+++ b/src/test/java/org/mockitointegration/NoJUnitDependenciesTest.java
@@ -42,27 +42,18 @@ public void pure_mockito_should_not_depend_JUnit___ByteBuddy() throws Exception
.omit("runners", "junit", "JUnit", "opentest4j")
.listOwnedClasses();
+ ClassLoadabilityChecker checker =
+ new ClassLoadabilityChecker(classLoader_without_JUnit, "JUnit");
+
// The later class is required to be initialized before any inline mock maker classes can be
// loaded.
- checkDependency(
- classLoader_without_JUnit,
+ checker.checkLoadability(
"org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker");
pureMockitoAPIClasses.remove(
"org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker");
for (String pureMockitoAPIClass : pureMockitoAPIClasses) {
- checkDependency(classLoader_without_JUnit, pureMockitoAPIClass);
- }
- }
-
- private void checkDependency(ClassLoader classLoader_without_JUnit, String pureMockitoAPIClass)
- throws ClassNotFoundException {
- try {
- Class.forName(pureMockitoAPIClass, true, classLoader_without_JUnit);
- } catch (Throwable e) {
- e.printStackTrace();
- throw new AssertionError(
- String.format("'%s' has some dependency to JUnit", pureMockitoAPIClass));
+ checker.checkLoadability(pureMockitoAPIClass);
}
}
}
diff --git a/src/test/java/org/mockitousage/IMethods.java b/src/test/java/org/mockitousage/IMethods.java
index 06492f09cb..5c1d3d8e5d 100644
--- a/src/test/java/org/mockitousage/IMethods.java
+++ b/src/test/java/org/mockitousage/IMethods.java
@@ -187,10 +187,12 @@ String sixArgumentVarArgsMethod(
int varargs(Object... object);
- String varargsReturningString(Object... object);
-
int varargs(String... string);
+ int polyVararg(BaseType... args);
+
+ String varargsReturningString(Object... object);
+
void mixedVarargs(Object i, String... string);
String mixedVarargsReturningString(Object i, String... string);
@@ -199,6 +201,10 @@ String sixArgumentVarArgsMethod(
Object[] mixedVarargsReturningObjectArray(Object i, String... string);
+ String methodWithVarargAndNonVarargVariants(String string);
+
+ String methodWithVarargAndNonVarargVariants(String... string);
+
List listReturningMethod(Object... objects);
LinkedList linkedListReturningMethod();
@@ -306,4 +312,6 @@ public int hashCode() {
return Objects.hash(value);
}
}
+
+ interface BaseType {}
}
diff --git a/src/test/java/org/mockitousage/MethodsImpl.java b/src/test/java/org/mockitousage/MethodsImpl.java
index e2d53ba7ba..678d3f7fe1 100644
--- a/src/test/java/org/mockitousage/MethodsImpl.java
+++ b/src/test/java/org/mockitousage/MethodsImpl.java
@@ -354,14 +354,19 @@ public int varargs(Object... object) {
return -1;
}
- public String varargsReturningString(Object... object) {
- return null;
- }
-
public int varargs(String... string) {
return -1;
}
+ @Override
+ public int polyVararg(final BaseType... args) {
+ return 0;
+ }
+
+ public String varargsReturningString(Object... object) {
+ return null;
+ }
+
public void mixedVarargs(Object i, String... string) {}
public String mixedVarargsReturningString(Object i, String... string) {
@@ -376,6 +381,16 @@ public Object[] mixedVarargsReturningObjectArray(Object i, String... string) {
return null;
}
+ @Override
+ public String methodWithVarargAndNonVarargVariants(String string) {
+ return "plain";
+ }
+
+ @Override
+ public String methodWithVarargAndNonVarargVariants(String... string) {
+ return "varargs";
+ }
+
public void varargsbyte(byte... bytes) {}
public List listReturningMethod(Object... objects) {
diff --git a/src/test/java/org/mockitousage/annotation/AnnotationsTest.java b/src/test/java/org/mockitousage/annotation/AnnotationsTest.java
index 1a33f933b2..939888a73d 100644
--- a/src/test/java/org/mockitousage/annotation/AnnotationsTest.java
+++ b/src/test/java/org/mockitousage/annotation/AnnotationsTest.java
@@ -88,6 +88,9 @@ public void shouldLookForAnnotatedMocksInSuperClasses() throws Exception {
@Mock(stubOnly = true)
IMethods stubOnly;
+ @Mock(withoutAnnotations = true)
+ IMethods withoutAnnotations;
+
@Test
public void shouldInitMocksWithGivenSettings() throws Exception {
assertEquals("i have a name", namedAndReturningMocks.toString());
@@ -99,6 +102,11 @@ public void shouldInitMocksWithGivenSettings() throws Exception {
assertTrue(hasExtraInterfaces instanceof List);
assertTrue(Mockito.mockingDetails(stubOnly).getMockCreationSettings().isStubOnly());
+ assertTrue(
+ Mockito.mockingDetails(withoutAnnotations)
+ .getMockCreationSettings()
+ .isStripAnnotations());
+
assertEquals(0, noExtraConfig.intReturningMethod());
}
diff --git a/src/test/java/org/mockitousage/annotation/SpyAnnotationTest.java b/src/test/java/org/mockitousage/annotation/SpyAnnotationTest.java
index 01edd3c687..daa71bed33 100644
--- a/src/test/java/org/mockitousage/annotation/SpyAnnotationTest.java
+++ b/src/test/java/org/mockitousage/annotation/SpyAnnotationTest.java
@@ -6,6 +6,8 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -22,6 +24,7 @@
import java.util.LinkedList;
import java.util.List;
+import org.junit.Assume;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -30,6 +33,8 @@
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.configuration.plugins.Plugins;
+import org.mockito.plugins.InlineMockMaker;
import org.mockitoutil.TestBase;
@SuppressWarnings("unused")
@@ -188,8 +193,21 @@ class WithSpy {
}
}
+ @Test
+ public void should_spy_private_inner() throws Exception {
+ Assume.assumeThat(Plugins.getMockMaker(), instanceOf(InlineMockMaker.class));
+
+ WithInnerPrivate inner = new WithInnerPrivate();
+ MockitoAnnotations.openMocks(inner);
+
+ when(inner.spy_field.lenght()).thenReturn(10);
+ assertEquals(10, inner.spy_field.lenght());
+ }
+
@Test
public void should_report_private_inner_not_supported() throws Exception {
+ Assume.assumeThat(Plugins.getMockMaker(), not(instanceOf(InlineMockMaker.class)));
+
try {
MockitoAnnotations.openMocks(new WithInnerPrivate());
fail();
@@ -287,7 +305,11 @@ private class InnerPrivateConcrete extends InnerPrivateAbstract {}
static class WithInnerPrivate {
@Spy private InnerPrivate spy_field;
- private class InnerPrivate {}
+ private class InnerPrivate {
+ int lenght() {
+ return 0;
+ }
+ }
private class InnerPrivateSub extends InnerPrivate {}
}
diff --git a/src/test/java/org/mockitousage/basicapi/UsingVarargsTest.java b/src/test/java/org/mockitousage/basicapi/UsingVarargsTest.java
index 551c63790a..8d69a2a52f 100644
--- a/src/test/java/org/mockitousage/basicapi/UsingVarargsTest.java
+++ b/src/test/java/org/mockitousage/basicapi/UsingVarargsTest.java
@@ -173,9 +173,9 @@ public void shouldStubCorrectlyWhenDoubleStringAndMixedVarargsUsed() {
@Test
// See bug #157
- public void shouldMatchEasilyEmptyVararg() throws Exception {
+ public void shouldMatchEasilyEmptyVararg() {
// when
- when(mock.foo(any())).thenReturn(-1);
+ when(mock.foo(any(Object[].class))).thenReturn(-1);
// then
assertEquals(-1, mock.foo());
diff --git a/src/test/java/org/mockitousage/bugs/ThreadLocalTest.java b/src/test/java/org/mockitousage/bugs/ThreadLocalTest.java
new file mode 100644
index 0000000000..8c113d90a6
--- /dev/null
+++ b/src/test/java/org/mockitousage/bugs/ThreadLocalTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockitousage.bugs;
+
+import static org.mockito.Mockito.RETURNS_MOCKS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+import org.mockitoutil.TestBase;
+
+/**
+ * This was an issue reported in #2905. Mocking {@link ThreadLocal} or classes extending {@link ThreadLocal} was
+ * throwing a {@link StackOverflowError}.
+ */
+public class ThreadLocalTest extends TestBase {
+
+ @Test
+ public void mock_ThreadLocal_does_not_raise_StackOverflowError() {
+ StackOverflowError stackOverflowError =
+ Assertions.catchThrowableOfType(
+ () -> {
+ mock(ThreadLocal.class, RETURNS_MOCKS);
+ },
+ StackOverflowError.class);
+ Assertions.assertThat(stackOverflowError).isNull();
+ }
+
+ @Test
+ public void mock_class_extending_ThreadLocal_does_not_raise_StackOverflowError() {
+ StackOverflowError stackOverflowError =
+ Assertions.catchThrowableOfType(
+ () -> {
+ mock(SomeThreadLocal.class, RETURNS_MOCKS);
+ },
+ StackOverflowError.class);
+ Assertions.assertThat(stackOverflowError).isNull();
+ }
+
+ @Test
+ public void spy_ThreadLocal_does_not_raise_StackOverflowError() {
+ StackOverflowError stackOverflowError =
+ Assertions.catchThrowableOfType(
+ () -> {
+ spy(ThreadLocal.class);
+ },
+ StackOverflowError.class);
+ Assertions.assertThat(stackOverflowError).isNull();
+ }
+
+ @Test
+ public void spy_class_extending_ThreadLocal_does_not_raise_StackOverflowError() {
+ StackOverflowError stackOverflowError =
+ Assertions.catchThrowableOfType(
+ () -> {
+ spy(SomeThreadLocal.class);
+ },
+ StackOverflowError.class);
+ Assertions.assertThat(stackOverflowError).isNull();
+ }
+
+ static class SomeThreadLocal extends ThreadLocal {}
+}
diff --git a/src/test/java/org/mockitousage/bugs/varargs/VarargsAndAnyPicksUpExtraInvocationsTest.java b/src/test/java/org/mockitousage/bugs/varargs/VarargsAndAnyPicksUpExtraInvocationsTest.java
index f6f4417c4a..c08bdf0643 100644
--- a/src/test/java/org/mockitousage/bugs/varargs/VarargsAndAnyPicksUpExtraInvocationsTest.java
+++ b/src/test/java/org/mockitousage/bugs/varargs/VarargsAndAnyPicksUpExtraInvocationsTest.java
@@ -26,7 +26,7 @@ public void shouldVerifyCorrectlyWithAny() {
table.newRow("abc", "def");
// then
- verify(table, times(2)).newRow(anyString(), (String[]) any());
+ verify(table, times(2)).newRow(anyString(), any(String[].class));
}
@Test
@@ -36,7 +36,7 @@ public void shouldVerifyCorrectlyNumberOfInvocationsUsingAnyAndEqualArgument() {
table.newRow("x", "def");
// then
- verify(table, times(2)).newRow(eq("x"), (String[]) any());
+ verify(table, times(2)).newRow(eq("x"), any(String[].class));
}
@Test
diff --git a/src/test/java/org/mockitousage/bugs/varargs/VarargsNotPlayingWithAnyTest.java b/src/test/java/org/mockitousage/bugs/varargs/VarargsNotPlayingWithAnyTest.java
deleted file mode 100644
index 6e11a6a7fd..0000000000
--- a/src/test/java/org/mockitousage/bugs/varargs/VarargsNotPlayingWithAnyTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2007 Mockito contributors
- * This program is made available under the terms of the MIT License.
- */
-package org.mockitousage.bugs.varargs;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockitoutil.TestBase;
-
-// see issue 62
-public class VarargsNotPlayingWithAnyTest extends TestBase {
-
- interface VarargMethod {
- Object run(String... args);
- }
-
- @Mock VarargMethod mock;
-
- @Test
- public void shouldMatchAny() {
- mock.run("a", "b");
-
- verify(mock).run(anyString(), anyString());
- verify(mock).run((String) any(), (String) any());
-
- verify(mock).run((String[]) any());
-
- verify(mock, never()).run();
- verify(mock, never()).run(anyString(), eq("f"));
- }
-
- @Test
- public void shouldAllowUsinganyForVarArgs() {
- mock.run("a", "b");
- verify(mock).run((String[]) any());
- }
-
- @Test
- public void shouldStubUsingAny() {
- when(mock.run((String[]) any())).thenReturn("foo");
-
- assertEquals("foo", mock.run("a", "b"));
- }
-}
diff --git a/src/test/java/org/mockitousage/configuration/ClassCacheVersusClassReloadingTest.java b/src/test/java/org/mockitousage/configuration/ClassCacheVersusClassReloadingTest.java
index 7d9c95061f..6c08198864 100644
--- a/src/test/java/org/mockitousage/configuration/ClassCacheVersusClassReloadingTest.java
+++ b/src/test/java/org/mockitousage/configuration/ClassCacheVersusClassReloadingTest.java
@@ -74,7 +74,9 @@ private static SimplePerRealmReloadingClassLoader.ReloadClassPredicate reloadMoc
return new SimplePerRealmReloadingClassLoader.ReloadClassPredicate() {
public boolean acceptReloadOf(String qualifiedName) {
return (!qualifiedName.contains("net.bytebuddy")
- && qualifiedName.contains("org.mockito"));
+ && qualifiedName.contains("org.mockito")
+ && !qualifiedName.contains(
+ "org.mockito.internal.creation.bytebuddy.inject"));
}
};
}
diff --git a/src/test/java/org/mockitousage/internal/debugging/LocationFactoryTest.java b/src/test/java/org/mockitousage/internal/debugging/LocationFactoryTest.java
new file mode 100644
index 0000000000..072c13fc02
--- /dev/null
+++ b/src/test/java/org/mockitousage/internal/debugging/LocationFactoryTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockitousage.internal.debugging;
+
+import org.junit.Test;
+import org.mockito.internal.debugging.LocationFactory;
+import org.mockitoutil.TestBase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+
+public class LocationFactoryTest extends TestBase {
+
+ @Test
+ public void shouldLocationNotContainGetStackTraceMethod() {
+ assertThat(LocationFactory.create().toString())
+ .contains("shouldLocationNotContainGetStackTraceMethod");
+ }
+
+ @Test
+ public void provides_location_class() {
+ // when
+ final List files = new ArrayList();
+ new Runnable() { // anonymous inner class adds stress to the check
+ public void run() {
+ files.add(LocationFactory.create().getSourceFile());
+ }
+ }.run();
+
+ // then
+ assertEquals("LocationFactoryTest.java", files.get(0));
+ }
+}
diff --git a/src/test/java/org/mockitousage/internal/debugging/LocationImplTest.java b/src/test/java/org/mockitousage/internal/debugging/LocationImplTest.java
deleted file mode 100644
index d20bfce02a..0000000000
--- a/src/test/java/org/mockitousage/internal/debugging/LocationImplTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2007 Mockito contributors
- * This program is made available under the terms of the MIT License.
- */
-package org.mockitousage.internal.debugging;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.assertEquals;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.junit.Test;
-import org.mockito.internal.debugging.LocationImpl;
-import org.mockito.internal.exceptions.stacktrace.StackTraceFilter;
-import org.mockitoutil.TestBase;
-
-@SuppressWarnings("serial")
-public class LocationImplTest extends TestBase {
-
- @Test
- public void shouldLocationNotContainGetStackTraceMethod() {
- assertThat(new LocationImpl().toString())
- .contains("shouldLocationNotContainGetStackTraceMethod");
- }
-
- @Test
- public void shouldBeSafeInCaseForSomeReasonFilteredStackTraceIsEmpty() {
- // given
- StackTraceFilter filterReturningEmptyArray =
- new StackTraceFilter() {
- @Override
- public StackTraceElement[] filter(StackTraceElement[] target, boolean keepTop) {
- return new StackTraceElement[0];
- }
-
- @Override
- public StackTraceElement filterFirst(Throwable target, boolean isInline) {
- return null;
- }
- };
-
- // when
- String loc = new LocationImpl(filterReturningEmptyArray).toString();
-
- // then
- assertEquals("-> at <>", loc);
- }
-
- @Test
- public void provides_location_class() {
- // when
- final List files = new ArrayList();
- new Runnable() { // anonymous inner class adds stress to the check
- public void run() {
- files.add(new LocationImpl().getSourceFile());
- }
- }.run();
-
- // then
- assertEquals("LocationImplTest.java", files.get(0));
- }
-}
diff --git a/src/test/java/org/mockitousage/matchers/CapturingArgumentsTest.java b/src/test/java/org/mockitousage/matchers/CapturingArgumentsTest.java
index b4b6e1a335..e0146de780 100644
--- a/src/test/java/org/mockitousage/matchers/CapturingArgumentsTest.java
+++ b/src/test/java/org/mockitousage/matchers/CapturingArgumentsTest.java
@@ -4,16 +4,19 @@
*/
package org.mockitousage.matchers;
+import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import java.util.Set;
-import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.exceptions.verification.WantedButNotInvoked;
import org.mockitousage.IMethods;
@@ -21,7 +24,7 @@
public class CapturingArgumentsTest extends TestBase {
- class Person {
+ private static class Person {
private final Integer age;
@@ -34,9 +37,9 @@ public int getAge() {
}
}
- class BulkEmailService {
+ private static class BulkEmailService {
- private EmailService service;
+ private final EmailService service;
public BulkEmailService(EmailService service) {
this.service = service;
@@ -54,11 +57,11 @@ interface EmailService {
boolean sendEmailTo(Person person);
}
- EmailService emailService = mock(EmailService.class);
- BulkEmailService bulkEmailService = new BulkEmailService(emailService);
- IMethods mock = mock(IMethods.class);
+ private final EmailService emailService = mock(EmailService.class);
+ private final BulkEmailService bulkEmailService = new BulkEmailService(emailService);
+ private final IMethods mock = mock(IMethods.class);
+ @Captor private ArgumentCaptor> listCaptor;
- @SuppressWarnings("deprecation")
@Test
public void should_allow_assertions_on_captured_argument() {
// given
@@ -110,7 +113,7 @@ public void should_print_captor_matcher() {
fail();
} catch (WantedButNotInvoked e) {
// then
- assertThat(e).hasMessageContaining("");
+ assertThat(e).hasMessageContaining("");
}
}
@@ -124,7 +127,7 @@ public void should_allow_assertions_on_captured_null() {
// then
verify(emailService).sendEmailTo(argument.capture());
- assertEquals(null, argument.getValue());
+ assertNull(argument.getValue());
}
@Test
@@ -135,6 +138,7 @@ public void should_allow_construction_of_captor_for_parameterized_type_in_a_conv
assertNotNull(argument);
}
+ @SuppressWarnings("unchecked")
@Test
public void should_allow_construction_of_captor_for_a_more_specific_type() {
// the test passes if this expression compiles
@@ -166,7 +170,7 @@ public void should_capture_when_stubbing_only_when_entire_invocation_matches() {
mock.simpleMethod("bar", 2);
// then
- Assertions.assertThat(argument.getAllValues()).containsOnly("bar");
+ assertThat(argument.getAllValues()).containsOnly("bar");
}
@Test
@@ -180,7 +184,7 @@ public void should_say_something_smart_when_misused() {
}
@Test
- public void should_capture_when_full_arg_list_matches() throws Exception {
+ public void should_capture_when_full_arg_list_matches() {
// given
ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
@@ -208,7 +212,7 @@ public void should_capture_int_by_creating_captor_with_primitive_wrapper() {
}
@Test
- public void should_capture_int_by_creating_captor_with_primitive() throws Exception {
+ public void should_capture_int_by_creating_captor_with_primitive() {
// given
ArgumentCaptor argument = ArgumentCaptor.forClass(int.class);
@@ -221,66 +225,227 @@ public void should_capture_int_by_creating_captor_with_primitive() throws Except
}
@Test
- public void should_capture_byte_vararg_by_creating_captor_with_primitive() throws Exception {
+ public void should_not_capture_int_by_creating_captor_with_primitive() {
+ // given
+ ArgumentCaptor argument = ArgumentCaptor.forClass(int.class);
+
+ // when
+ mock.forObject(10L);
+
+ // then
+ verify(mock, never()).forObject(argument.capture());
+ }
+
+ @Test
+ public void should_capture_byte_vararg_by_creating_captor_with_primitive() {
// given
ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(byte.class);
// when
- mock.varargsbyte((byte) 1, (byte) 2);
+ mock.varargsbyte((byte) 1);
// then
verify(mock).varargsbyte(argumentCaptor.capture());
+ assertEquals((byte) 1, (byte) argumentCaptor.getValue());
+ assertThat(argumentCaptor.getAllValues()).containsExactly((byte) 1);
+ }
+
+ @Test
+ public void should_capture_byte_vararg_by_creating_captor_with_primitive_2_args() {
+ // given
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(byte.class);
+
+ // when
+ mock.varargsbyte((byte) 1, (byte) 2);
+
+ // then
+ verify(mock).varargsbyte(argumentCaptor.capture(), argumentCaptor.capture());
assertEquals((byte) 2, (byte) argumentCaptor.getValue());
- Assertions.assertThat(argumentCaptor.getAllValues()).containsExactly((byte) 1, (byte) 2);
+ assertThat(argumentCaptor.getAllValues()).containsExactly((byte) 1, (byte) 2);
}
@Test
- public void should_capture_byte_vararg_by_creating_captor_with_primitive_wrapper()
- throws Exception {
+ public void should_capture_byte_vararg_by_creating_captor_with_primitive_array() {
// given
- ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Byte.class);
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(byte[].class);
// when
+ mock.varargsbyte();
mock.varargsbyte((byte) 1, (byte) 2);
+ // then
+ verify(mock, times(2)).varargsbyte(argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue()).containsExactly(new byte[] {1, 2});
+ assertThat(argumentCaptor.getAllValues()).containsExactly(new byte[] {}, new byte[] {1, 2});
+ }
+
+ @Test
+ public void should_capture_byte_vararg_by_creating_captor_with_primitive_wrapper() {
+ // given
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Byte.class);
+
+ // when
+ mock.varargsbyte((byte) 1);
+
// then
verify(mock).varargsbyte(argumentCaptor.capture());
- assertEquals((byte) 2, (byte) argumentCaptor.getValue());
- Assertions.assertThat(argumentCaptor.getAllValues()).containsExactly((byte) 1, (byte) 2);
+ assertEquals((byte) 1, (byte) argumentCaptor.getValue());
+ assertThat(argumentCaptor.getAllValues()).containsExactly((byte) 1);
}
@Test
- public void should_capture_vararg() throws Exception {
+ public void should_not_capture_empty_vararg_with_single_captor() {
// given
ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
// when
- mock.mixedVarargs(42, "a", "b", "c");
+ mock.mixedVarargs(42);
+
+ // then
+ verify(mock, never()).mixedVarargs(any(), argumentCaptor.capture());
+ }
+
+ @Test
+ public void should_capture_single_vararg_with_single_captor() {
+ // given
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
+
+ // when
+ mock.mixedVarargs(42, "a");
// then
verify(mock).mixedVarargs(any(), argumentCaptor.capture());
- Assertions.assertThat(argumentCaptor.getAllValues()).containsExactly("a", "b", "c");
+ assertThat(argumentCaptor.getValue()).isEqualTo("a");
+ }
+
+ @Test
+ public void should_not_capture_multiple_vararg_with_single_captor() {
+ // given
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
+
+ // when
+ mock.mixedVarargs(42, "a", "b");
+
+ // then
+ verify(mock, never()).mixedVarargs(any(), argumentCaptor.capture());
}
@Test
- public void should_capture_all_vararg() throws Exception {
+ public void should_capture_multiple_vararg_with_multiple_captor() {
// given
ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
+ // when
+ mock.mixedVarargs(42, "a", "b");
+
+ // then
+ verify(mock).mixedVarargs(any(), argumentCaptor.capture(), argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue()).isEqualTo("b");
+ assertThat(argumentCaptor.getAllValues()).isEqualTo(asList("a", "b"));
+ }
+
+ @Test
+ public void should_not_capture_multiple_vararg_some_null_with_single_captor() {
+ // given
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
+
+ // when
+ mock.mixedVarargs(42, "a", null);
+
+ // then
+ verify(mock, never()).mixedVarargs(any(), argumentCaptor.capture());
+ }
+
+ @Test
+ public void should_capture_empty_vararg_with_array_captor() {
+ // given
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class);
+
+ // when
+ mock.mixedVarargs(42);
+
+ // then
+ verify(mock).mixedVarargs(any(), argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue()).isEqualTo(new String[] {});
+ assertThat(argumentCaptor.getAllValues()).containsExactly(new String[] {});
+ }
+
+ @Test
+ public void should_capture_single_vararg_with_array_captor() {
+ // given
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class);
+
+ // when
+ mock.mixedVarargs(42, "a");
+
+ // then
+ verify(mock).mixedVarargs(any(), argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue()).isEqualTo(new String[] {"a"});
+ assertThat(argumentCaptor.getAllValues()).containsExactly(new String[] {"a"});
+ }
+
+ @Test
+ public void should_capture_multiple_vararg_with_array_captor() {
+ // given
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class);
+
// when
mock.mixedVarargs(42, "a", "b", "c");
- mock.mixedVarargs(42, "again ?!");
+
+ // then
+ verify(mock).mixedVarargs(any(), argumentCaptor.capture());
+ assertThat(argumentCaptor.getAllValues()).containsExactly(new String[] {"a", "b", "c"});
+ }
+
+ @Test
+ public void should_capture_multiple_vararg_some_null_with_array_captor() {
+ // given
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class);
+
+ // when
+ mock.mixedVarargs(42, "a", null, "c");
+
+ // then
+ verify(mock).mixedVarargs(any(), argumentCaptor.capture());
+ assertThat(argumentCaptor.getAllValues()).containsExactly(new String[] {"a", null, "c"});
+ }
+
+ @Test
+ public void should_capture_multiple_invocations_with_captor() {
+ // given
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
+
+ // when
+ mock.mixedVarargs(42, "a", "b");
+ mock.mixedVarargs(42, "c", "d");
+
+ // then
+ verify(mock, times(2))
+ .mixedVarargs(any(), argumentCaptor.capture(), argumentCaptor.capture());
+
+ assertThat(argumentCaptor.getValue()).isEqualTo("d");
+ assertThat(argumentCaptor.getAllValues()).containsExactly("a", "b", "c", "d");
+ }
+
+ @Test
+ public void should_capture_multiple_invocations_with_array_captor() {
+ // given
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class);
+
+ // when
+ mock.mixedVarargs(42, "a", "b");
+ mock.mixedVarargs(42, "c", "d");
// then
verify(mock, times(2)).mixedVarargs(any(), argumentCaptor.capture());
- Assertions.assertThat(argumentCaptor.getAllValues())
- .containsExactly("a", "b", "c", "again ?!");
+ assertThat(argumentCaptor.getValue()).isEqualTo(new String[] {"c", "d"});
+ assertThat(argumentCaptor.getAllValues())
+ .containsExactly(new String[] {"a", "b"}, new String[] {"c", "d"});
}
@Test
- public void should_capture_one_arg_even_when_using_vararg_captor_on_nonvararg_method()
- throws Exception {
+ public void should_capture_one_arg_even_when_using_vararg_captor_on_nonvararg_method() {
// given
ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
@@ -289,11 +454,11 @@ public void should_capture_one_arg_even_when_using_vararg_captor_on_nonvararg_me
// then
verify(mock).simpleMethod(argumentCaptor.capture(), eq(2));
- Assertions.assertThat(argumentCaptor.getAllValues()).containsExactly("a");
+ assertThat(argumentCaptor.getAllValues()).containsExactly("a");
}
@Test
- public void captures_correctly_when_captor_used_multiple_times() throws Exception {
+ public void captures_correctly_when_captor_used_multiple_times() {
// given
ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
@@ -308,11 +473,11 @@ public void captures_correctly_when_captor_used_multiple_times() throws Exceptio
argumentCaptor.capture(),
argumentCaptor.capture(),
argumentCaptor.capture());
- Assertions.assertThat(argumentCaptor.getAllValues()).containsExactly("a", "b", "c");
+ assertThat(argumentCaptor.getAllValues()).containsExactly("a", "b", "c");
}
@Test
- public void captures_correctly_when_captor_used_on_pure_vararg_method() throws Exception {
+ public void captures_correctly_when_captor_used_on_pure_vararg_method() {
// given
ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
@@ -321,6 +486,43 @@ public void captures_correctly_when_captor_used_on_pure_vararg_method() throws E
// then
verify(mock).varargs(eq(42), argumentCaptor.capture());
- Assertions.assertThat(argumentCaptor.getValue()).contains("capturedValue");
+ assertThat(argumentCaptor.getValue()).contains("capturedValue");
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void should_capture_by_type() {
+ // When:
+ mock.simpleMethod(Set.of());
+ mock.simpleMethod(new ArrayList<>(0));
+
+ // Then:
+ ArgumentCaptor> captor = ArgumentCaptor.forClass(ArrayList.class);
+ verify(mock).simpleMethod(captor.capture());
+ assertThat(captor.getAllValues()).containsExactly(List.of());
+ }
+
+ @Test
+ public void should_capture_by_type_using_annotation() {
+ // When:
+ mock.simpleMethod(Set.of());
+ mock.simpleMethod(new ArrayList<>(0));
+
+ // Then:
+ verify(mock).simpleMethod(listCaptor.capture());
+ assertThat(listCaptor.getAllValues()).containsExactly(List.of());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void should_always_capture_nulls() {
+ // When:
+ mock.simpleMethod((Set>) null);
+ mock.simpleMethod((List>) null);
+
+ // Then:
+ ArgumentCaptor> captor = ArgumentCaptor.forClass(ArrayList.class);
+ verify(mock, times(2)).simpleMethod(captor.capture());
+ assertThat(captor.getAllValues()).containsExactly(null, null);
}
}
diff --git a/src/test/java/org/mockitousage/matchers/HamcrestMatchersTest.java b/src/test/java/org/mockitousage/matchers/HamcrestMatchersTest.java
index 3f16618c2f..261a3c3c96 100644
--- a/src/test/java/org/mockitousage/matchers/HamcrestMatchersTest.java
+++ b/src/test/java/org/mockitousage/matchers/HamcrestMatchersTest.java
@@ -6,7 +6,10 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.isA;
import static org.junit.Assert.*;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.*;
@@ -54,7 +57,44 @@ public void verifies_with_hamcrest_matcher() {
}
}
- private class IntMatcher extends BaseMatcher {
+ @Test
+ public void does_not_verify_vararg_with_no_items() {
+ mock.varargs();
+
+ verify(mock, never()).varargs(argThat(isA(String.class)));
+ }
+
+ @Test
+ public void verifies_vararg_with_single_item() {
+ mock.varargs("a");
+
+ verify(mock).varargs(argThat(isA(String.class)));
+ }
+
+ @Test
+ public void does_not_verify_vararg_with_multiple_items() {
+ mock.varargs("a", "b");
+
+ verify(mock, never()).varargs(argThat(isA(String.class)));
+ }
+
+ @Test
+ public void verify_vararg_with_multiple_item() {
+ mock.varargs("a", "b");
+
+ verify(mock).varargs(argThat(isA(String.class)), argThat(isA(String.class)));
+ }
+
+ @Test
+ public void verifies_vararg_with_any_num_items() {
+ mock.varargs();
+ mock.varargs("a");
+ mock.varargs("a", "b");
+
+ verify(mock, times(3)).varargs(argThat(isA(String[].class), String[].class));
+ }
+
+ private final class IntMatcher extends BaseMatcher {
public boolean matches(Object o) {
return true;
}
diff --git a/src/test/java/org/mockitousage/matchers/MatchersTest.java b/src/test/java/org/mockitousage/matchers/MatchersTest.java
index 777c49a063..f89b44b731 100644
--- a/src/test/java/org/mockitousage/matchers/MatchersTest.java
+++ b/src/test/java/org/mockitousage/matchers/MatchersTest.java
@@ -20,6 +20,7 @@
import static org.mockito.AdditionalMatchers.lt;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.AdditionalMatchers.or;
+import static org.mockito.ArgumentMatchers.assertArg;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
@@ -52,6 +53,7 @@
import java.util.RandomAccess;
import java.util.regex.Pattern;
+import org.junit.ComparisonFailure;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
@@ -624,4 +626,51 @@ public void nullable_matcher() throws Exception {
verify(mock, times(2)).oneArg(nullable(Character.class));
}
+
+ @Test
+ public void assertArg_matcher() throws Exception {
+ mock.oneArg("hello");
+
+ verify(mock).oneArg(assertArg((String it) -> assertEquals("hello", it)));
+ }
+
+ @Test
+ public void assertArg_matcher_fails_when_assertion_fails() throws Exception {
+ mock.oneArg("hello");
+
+ try {
+ verify(mock).oneArg(assertArg((String it) -> assertEquals("not-hello", it)));
+ fail("Should throw an exception");
+ } catch (ComparisonFailure e) {
+ // do nothing
+ }
+ }
+
+ @Test
+ public void can_invoke_method_on_mock_after_assert_arg() throws Exception {
+ mock.oneArg("hello");
+
+ try {
+ verify(mock).oneArg(assertArg((String it) -> assertEquals("not-hello", it)));
+ fail("Should throw an exception");
+ } catch (ComparisonFailure e) {
+ // do nothing
+ }
+
+ mock.oneArg("hello");
+ }
+
+ @Test
+ public void can_verify_on_mock_after_assert_arg() throws Exception {
+ mock.oneArg("hello");
+
+ try {
+ verify(mock).oneArg(assertArg((String it) -> assertEquals("not-hello", it)));
+ fail("Should throw an exception");
+ } catch (ComparisonFailure e) {
+ // do nothing
+ }
+
+ verify(mock).oneArg("hello");
+ }
}
diff --git a/src/test/java/org/mockitousage/matchers/VarargsTest.java b/src/test/java/org/mockitousage/matchers/VarargsTest.java
index dfb726942b..5daba370eb 100644
--- a/src/test/java/org/mockitousage/matchers/VarargsTest.java
+++ b/src/test/java/org/mockitousage/matchers/VarargsTest.java
@@ -4,12 +4,18 @@
*/
package org.mockitousage.matchers;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.fail;
+import static org.mockito.AdditionalMatchers.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.ArgumentMatchers.isNotNull;
import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -28,11 +34,13 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockitousage.IMethods;
+import org.mockitousage.IMethods.BaseType;
public class VarargsTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Captor private ArgumentCaptor captor;
+ @Captor private ArgumentCaptor arrayCaptor;
@Mock private IMethods mock;
private static final Condition NULL =
@@ -52,27 +60,17 @@ public void shouldMatchVarArgs_noArgs() {
}
@Test
- @Ignore("This test must succeed but is fails currently, see github issue #616")
public void shouldMatchEmptyVarArgs_noArgsIsNotNull() {
mock.varargs();
- verify(mock).varargs(isNotNull());
+ verify(mock).varargs(isNotNull(String[].class));
}
@Test
- @Ignore("This test must succeed but is fails currently, see github issue #616")
public void shouldMatchEmptyVarArgs_noArgsIsNull() {
- mock.varargs();
-
- verify(mock).varargs(isNull());
- }
-
- @Test
- @Ignore("This test must succeed but is fails currently, see github issue #616")
- public void shouldMatchEmptyVarArgs_noArgsIsNotNullArray() {
- mock.varargs();
+ mock.varargs((String[]) null);
- verify(mock).varargs((String[]) isNotNull());
+ verify(mock).varargs(isNull(String[].class));
}
@Test
@@ -85,18 +83,18 @@ public void shouldMatchVarArgs_oneNullArg_eqNull() {
@Test
public void shouldMatchVarArgs_oneNullArg_isNull() {
- Object arg = null;
- mock.varargs(arg);
+ mock.varargs((Object) null);
- verify(mock).varargs(ArgumentMatchers.isNull());
+ verify(mock).varargs(ArgumentMatchers.isNull());
+ verify(mock, never()).varargs(isNull(Object[].class));
}
@Test
public void shouldMatchVarArgs_nullArrayArg() {
- Object[] argArray = null;
- mock.varargs(argArray);
+ mock.varargs((Object[]) null);
- verify(mock).varargs(ArgumentMatchers.isNull());
+ verify(mock).varargs(isNull(Object[].class));
+ verify(mock).varargs(ArgumentMatchers.isNull());
}
@Test
@@ -114,28 +112,28 @@ public void shouldnotMatchVarArgs_twoArgsOneMatcher() {
public void shouldMatchVarArgs_emptyVarArgsOneAnyMatcher() {
mock.varargs();
- verify(mock).varargs((String[]) any()); // any() -> VarargMatcher
+ verify(mock).varargs(any(String[].class));
}
@Test
public void shouldMatchVarArgs_oneArgsOneAnyMatcher() {
mock.varargs(1);
- verify(mock).varargs(ArgumentMatchers.any()); // any() -> VarargMatcher
+ verify(mock).varargs(any(Object[].class));
}
@Test
public void shouldMatchVarArgs_twoArgsOneAnyMatcher() {
mock.varargs(1, 2);
- verify(mock).varargs(ArgumentMatchers.any()); // any() -> VarargMatcher
+ verify(mock).varargs(any(Object[].class));
}
@Test
public void shouldMatchVarArgs_twoArgsTwoAnyMatcher() {
mock.varargs(1, 2);
- verify(mock).varargs(any(), ArgumentMatchers.any()); // any() -> VarargMatcher
+ verify(mock).varargs(any(), ArgumentMatchers.any());
}
@Test
@@ -144,7 +142,7 @@ public void shouldMatchVarArgs_twoArgsThreeAnyMatcher() {
assertThatThrownBy(
() -> {
- verify(mock).varargs(any(), any(), any()); // any() -> VarargMatcher
+ verify(mock).varargs(any(), any(), any());
})
.hasMessageContaining("Argument(s) are different");
}
@@ -178,11 +176,10 @@ public void shouldMatchVarArgs_emptyByteArray() {
}
@Test
- @Ignore
public void shouldMatchEmptyVarArgs_emptyArrayIsNotNull() {
mock.varargsbyte();
- verify(mock).varargsbyte((byte[]) isNotNull());
+ verify(mock).varargsbyte(isNotNull(byte[].class));
}
@Test
@@ -196,9 +193,9 @@ public void shouldMatchVarArgs_oneArgIsNotNull() {
public void shouldCaptureVarArgs_noArgs() {
mock.varargs();
- verify(mock).varargs(captor.capture());
+ verify(mock).varargs(arrayCaptor.capture());
- assertThat(captor).isEmpty();
+ assertThatCaptor(arrayCaptor).contains(new String[] {});
}
@Test
@@ -208,7 +205,7 @@ public void shouldCaptureVarArgs_oneNullArg_eqNull() {
verify(mock).varargs(captor.capture());
- assertThat(captor).areExactly(1, NULL);
+ assertThatCaptor(captor).areExactly(1, NULL);
}
/**
@@ -221,16 +218,16 @@ public void shouldCaptureVarArgs_nullArrayArg() {
mock.varargs(argArray);
verify(mock).varargs(captor.capture());
- assertThat(captor).areExactly(1, NULL);
+ assertThatCaptor(captor).areExactly(1, NULL);
}
@Test
public void shouldCaptureVarArgs_twoArgsOneCapture() {
mock.varargs("1", "2");
- verify(mock).varargs(captor.capture());
+ verify(mock).varargs(arrayCaptor.capture());
- assertThat(captor).contains("1", "2");
+ assertThatCaptor(arrayCaptor).contains(new String[] {"1", "2"});
}
@Test
@@ -239,16 +236,7 @@ public void shouldCaptureVarArgs_twoArgsTwoCaptures() {
verify(mock).varargs(captor.capture(), captor.capture());
- assertThat(captor).contains("1", "2");
- }
-
- @Test
- public void shouldCaptureVarArgs_oneNullArgument() {
- mock.varargs("1", null);
-
- verify(mock).varargs(captor.capture());
-
- assertThat(captor).contains("1", (String) null);
+ assertThatCaptor(captor).contains("1", "2");
}
@Test
@@ -257,7 +245,7 @@ public void shouldCaptureVarArgs_oneNullArgument2() {
verify(mock).varargs(captor.capture(), captor.capture());
- assertThat(captor).contains("1", (String) null);
+ assertThatCaptor(captor).contains("1", (String) null);
}
@Test
@@ -277,7 +265,7 @@ public void shouldCaptureVarArgs_3argsCaptorMatcherMix() {
verify(mock).varargs(captor.capture(), eq("2"), captor.capture());
- assertThat(captor).containsExactly("1", "3");
+ assertThatCaptor(captor).containsExactly("1", "3");
}
@Test
@@ -290,7 +278,7 @@ public void shouldNotCaptureVarArgs_3argsCaptorMatcherMix() {
} catch (ArgumentsAreDifferent expected) {
}
- assertThat(captor).isEmpty();
+ assertThatCaptor(captor).isEmpty();
}
@Test
@@ -304,16 +292,7 @@ public void shouldNotCaptureVarArgs_1args2captures() {
.isInstanceOf(ArgumentsAreDifferent.class);
}
- /**
- * As of v2.0.0-beta.118 this test fails. Once the github issues:
- *
- * '#584 ArgumentCaptor can't capture varargs-arrays
- * #565 ArgumentCaptor should be type aware' are fixed this test must
- * succeed
- *
- */
@Test
- @Ignore("Blocked by github issue: #584 & #565")
public void shouldCaptureVarArgsAsArray() {
mock.varargs("1", "2");
@@ -321,7 +300,7 @@ public void shouldCaptureVarArgsAsArray() {
verify(mock).varargs(varargCaptor.capture());
- assertThat(varargCaptor).containsExactly(new String[] {"1", "2"});
+ assertThatCaptor(varargCaptor).containsExactly(new String[] {"1", "2"});
}
@Test
@@ -342,8 +321,267 @@ public void shouldNotMatchVaraArgs() {
Assertions.assertThat(mock.varargsObject(1)).isNull();
}
- private static AbstractListAssert, ?, T, ObjectAssert> assertThat(
+ @Test
+ public void shouldDifferentiateNonVarargVariant() {
+ given(mock.methodWithVarargAndNonVarargVariants(any(String.class)))
+ .willReturn("single arg method");
+
+ assertThat(mock.methodWithVarargAndNonVarargVariants("a")).isEqualTo("single arg method");
+ assertThat(mock.methodWithVarargAndNonVarargVariants(new String[] {"a"})).isNull();
+ assertThat(mock.methodWithVarargAndNonVarargVariants("a", "b")).isNull();
+ }
+
+ @Test
+ public void shouldMockVarargsInvocation_single_vararg_matcher() {
+ given(mock.methodWithVarargAndNonVarargVariants(any(String[].class)))
+ .willReturn("var arg method");
+
+ assertThat(mock.methodWithVarargAndNonVarargVariants("a")).isNull();
+ assertThat(mock.methodWithVarargAndNonVarargVariants(new String[] {"a"}))
+ .isEqualTo("var arg method");
+ assertThat(mock.methodWithVarargAndNonVarargVariants("a", "b")).isEqualTo("var arg method");
+ }
+
+ @Test
+ public void shouldMockVarargsInvocation_multiple_vararg_matcher() {
+ given(mock.methodWithVarargAndNonVarargVariants(any(String.class), any(String.class)))
+ .willReturn("var arg method");
+
+ assertThat(mock.methodWithVarargAndNonVarargVariants("a")).isNull();
+ assertThat(mock.methodWithVarargAndNonVarargVariants(new String[] {"a"})).isNull();
+ assertThat(mock.methodWithVarargAndNonVarargVariants("a", "b")).isEqualTo("var arg method");
+ assertThat(mock.methodWithVarargAndNonVarargVariants(new String[] {"a", "b"}))
+ .isEqualTo("var arg method");
+ assertThat(mock.methodWithVarargAndNonVarargVariants("a", "b", "c")).isNull();
+ }
+
+ @Test
+ public void shouldMockVarargsInvocationForSuperType() {
+ given(mock.varargsReturningString(any(Object[].class))).willReturn("a");
+
+ assertThat(mock.varargsReturningString("a", "b")).isEqualTo("a");
+ }
+
+ @Test
+ public void shouldHandleArrayVarargsMethods() {
+ given(mock.arrayVarargsMethod(any(String[][].class))).willReturn(1);
+
+ assertThat(mock.arrayVarargsMethod(new String[] {})).isEqualTo(1);
+ }
+
+ @Test
+ public void shouldCaptureVarArgs_NullArrayArg1() {
+ mock.varargs((String[]) null);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(String[].class);
+
+ verify(mock).varargs(captor.capture());
+
+ assertThat(captor.getValue()).isNull();
+ }
+
+ @Test
+ public void shouldCaptureVarArgs_NullArrayArg2() {
+ mock.varargs((String[]) null);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
+
+ verify(mock).varargs(captor.capture());
+
+ assertThat(captor.getValue()).isNull();
+ }
+
+ @Test
+ public void shouldVerifyVarArgs_any_NullArrayArg1() {
+ mock.varargs((String[]) null);
+
+ verify(mock).varargs(any());
+ }
+
+ @Test
+ public void shouldVerifyVarArgs_any_NullArrayArg2() {
+ mock.varargs((String) null);
+
+ verify(mock).varargs(any());
+ }
+
+ @Test
+ public void shouldVerifyVarArgs_eq_NullArrayArg1() {
+ mock.varargs((String[]) null);
+
+ verify(mock).varargs(eq(null));
+ }
+
+ @Test
+ public void shouldVerifyVarArgs_eq_NullArrayArg2() {
+ mock.varargs((String) null);
+
+ verify(mock).varargs(eq(null));
+ }
+
+ @Test
+ public void shouldVerifyVarArgs_isNull_NullArrayArg() {
+ mock.varargs((String) null);
+
+ verify(mock).varargs(isNull(String.class));
+ }
+
+ @Test
+ public void shouldVerifyVarArgs_isNull_NullArrayArg2() {
+ mock.varargs((String) null);
+
+ verify(mock).varargs(isNull());
+ }
+
+ @Test
+ public void shouldVerifyExactlyOneVarArg_isA() {
+ mock.varargs("one param");
+
+ verify(mock).varargs(isA(String.class));
+ }
+
+ @Test
+ public void shouldNotVerifyExactlyOneVarArg_isA() {
+ mock.varargs("two", "params");
+
+ verify(mock, never()).varargs(isA(String.class));
+ }
+
+ @Test
+ public void shouldVerifyVarArgArray_isA() {
+ mock.varargs("one param");
+
+ verify(mock).varargs(isA(String[].class));
+ }
+
+ @Test
+ public void shouldVerifyVarArgArray_isA2() {
+ mock.varargs("two", "params");
+
+ verify(mock).varargs(isA(String[].class));
+ }
+
+ @Test
+ public void shouldVerifyExactlyOneVarArg_any() {
+ mock.varargs("one param");
+
+ verify(mock).varargs(any(String.class));
+ }
+
+ @Test
+ @Ignore("Fails due to https://github.com/mockito/mockito/issues/1593")
+ public void shouldNotVerifyExactlyOneVarArg_any() {
+ mock.varargs("two", "params");
+
+ verify(mock, never()).varargs(any(String.class));
+ }
+
+ @Test
+ public void shouldMockVarargInvocation_eq() {
+ given(mock.varargs(eq("one param"))).willReturn(1);
+
+ assertThat(mock.varargs("one param")).isEqualTo(1);
+ assertThat(mock.varargs()).isEqualTo(0);
+ assertThat(mock.varargs("different")).isEqualTo(0);
+ assertThat(mock.varargs("one param", "another")).isEqualTo(0);
+ }
+
+ @Test
+ public void shouldVerifyInvocation_eq() {
+ mock.varargs("one param");
+
+ verify(mock).varargs(eq("one param"));
+ verify(mock, never()).varargs();
+ verify(mock, never()).varargs(eq("different"));
+ verify(mock, never()).varargs(eq("one param"), eq("another"));
+ }
+
+ @Test
+ public void shouldMockVarargInvocation_eq_raw() {
+ given(mock.varargs(eq(new String[] {"one param"}))).willReturn(1);
+
+ assertThat(mock.varargs("one param")).isEqualTo(1);
+ assertThat(mock.varargs()).isEqualTo(0);
+ assertThat(mock.varargs("different")).isEqualTo(0);
+ assertThat(mock.varargs("one param", "another")).isEqualTo(0);
+ }
+
+ @Test
+ public void shouldVerifyInvocation_eq_raw() {
+ mock.varargs("one param");
+
+ verify(mock).varargs(eq(new String[] {"one param"}));
+ verify(mock, never()).varargs(eq(new String[] {}));
+ verify(mock, never()).varargs(eq(new String[] {"different"}));
+ verify(mock, never()).varargs(eq(new String[] {"one param", "another"}));
+ }
+
+ @Test
+ public void shouldVerifyInvocation_not() {
+ mock.varargs("one param");
+
+ verify(mock).varargs(not(eq(new String[] {"diff"})));
+ verify(mock, never()).varargs(not(eq(new String[] {"one param"})));
+ }
+
+ @Test
+ public void shouldVerifyInvocation_same() {
+ String[] args = {"two", "params"};
+
+ mock.varargs(args);
+
+ verify(mock).varargs(same(args));
+ verify(mock, never()).varargs(same(new String[] {"two", "params"}));
+ }
+
+ @Test
+ public void shouldVerifySubTypes() {
+ mock.polyVararg(new SubType(), new SubType());
+
+ verify(mock).polyVararg(eq(new SubType()), eq(new SubType()));
+ verify(mock).polyVararg(eq(new SubType[] {new SubType(), new SubType()}));
+ verify(mock).polyVararg(eq(new BaseType[] {new SubType(), new SubType()}));
+ }
+
+ @Test
+ public void shouldVerifyInvocation_or() {
+ mock.polyVararg(new SubType(), new SubType());
+
+ verify(mock)
+ .polyVararg(
+ or(
+ eq(new BaseType[] {new SubType()}),
+ eq(new SubType[] {new SubType(), new SubType()})));
+ verify(mock)
+ .polyVararg(
+ or(
+ eq(new BaseType[] {new SubType(), new SubType()}),
+ eq(new SubType[] {new SubType()})));
+ }
+
+ @Test
+ public void shouldVerifyInvocation_and() {
+ mock.polyVararg(new SubType(), new SubType());
+
+ verify(mock)
+ .polyVararg(
+ and(
+ eq(new BaseType[] {new SubType(), new SubType()}),
+ eq(new SubType[] {new SubType(), new SubType()})));
+ }
+
+ private static AbstractListAssert, ?, T, ObjectAssert> assertThatCaptor(
ArgumentCaptor captor) {
return Assertions.assertThat(captor.getAllValues());
}
+
+ private static class SubType implements BaseType {
+ @Override
+ public boolean equals(final Object obj) {
+ return obj != null && obj.getClass().equals(getClass());
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+ }
}
diff --git a/src/test/java/org/mockitousage/misuse/InvalidUsageTest.java b/src/test/java/org/mockitousage/misuse/InvalidUsageTest.java
index 35c135a62e..e4a8275be9 100644
--- a/src/test/java/org/mockitousage/misuse/InvalidUsageTest.java
+++ b/src/test/java/org/mockitousage/misuse/InvalidUsageTest.java
@@ -4,7 +4,10 @@
*/
package org.mockitousage.misuse;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.not;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoInteractions;
@@ -12,11 +15,14 @@
import static org.mockito.Mockito.when;
import org.junit.After;
+import org.junit.Assume;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.exceptions.misusing.MissingMethodInvocationException;
+import org.mockito.internal.configuration.plugins.Plugins;
+import org.mockito.plugins.InlineMockMaker;
import org.mockitousage.IMethods;
import org.mockitoutil.TestBase;
@@ -155,6 +161,8 @@ final class FinalClass {}
@Test
public void shouldNotAllowMockingFinalClassesIfDisabled() {
+ Assume.assumeThat(Plugins.getMockMaker(), not(instanceOf(InlineMockMaker.class)));
+
assertThatThrownBy(
() -> {
mock(FinalClass.class);
@@ -166,6 +174,12 @@ public void shouldNotAllowMockingFinalClassesIfDisabled() {
" - final class");
}
+ @Test
+ public void shouldAllowMockingFinalClassesIfEnabled() {
+ Assume.assumeThat(Plugins.getMockMaker(), instanceOf(InlineMockMaker.class));
+ assertThat(mock(FinalClass.class)).isInstanceOf(FinalClass.class);
+ }
+
@SuppressWarnings({"CheckReturnValue", "MockitoUsage"})
@Test
public void shouldNotAllowMockingPrimitives() {
diff --git a/src/test/java/org/mockitousage/spies/PartialMockingWithSpiesTest.java b/src/test/java/org/mockitousage/spies/PartialMockingWithSpiesTest.java
index bf616f8d9f..4efcb2b275 100644
--- a/src/test/java/org/mockitousage/spies/PartialMockingWithSpiesTest.java
+++ b/src/test/java/org/mockitousage/spies/PartialMockingWithSpiesTest.java
@@ -4,6 +4,8 @@
*/
package org.mockitousage.spies;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doThrow;
@@ -13,8 +15,12 @@
import static org.mockitoutil.Conditions.methodsInStackTrace;
import org.assertj.core.api.Assertions;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.internal.configuration.plugins.Plugins;
+import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker;
+import org.mockito.internal.util.reflection.ReflectionMemberAccessor;
import org.mockitoutil.TestBase;
@SuppressWarnings("unchecked")
@@ -104,6 +110,9 @@ public void shouldAllowStubbingWithThrowablesMethodsThatDelegateToOtherMethods()
@Test
public void shouldStackTraceGetFilteredOnUserExceptions() {
+ Assume.assumeThat(
+ Plugins.getMemberAccessor(), not(instanceOf(ReflectionMemberAccessor.class)));
+
try {
// when
spy.getNameButDelegateToMethodThatThrows();
@@ -119,6 +128,30 @@ public void shouldStackTraceGetFilteredOnUserExceptions() {
}
}
+ @Test
+ public void shouldStackTraceGetFilteredOnUserExceptionsReflection() {
+ Assume.assumeThat(Plugins.getMockMaker(), instanceOf(InlineByteBuddyMockMaker.class));
+ Assume.assumeThat(Plugins.getMemberAccessor(), instanceOf(ReflectionMemberAccessor.class));
+
+ try {
+ // when
+ spy.getNameButDelegateToMethodThatThrows();
+ fail();
+ } catch (Throwable t) {
+ // then
+ Assertions.assertThat(t)
+ .has(
+ methodsInStackTrace(
+ "throwSomeException",
+ "invoke0",
+ "invoke",
+ "invoke",
+ "invoke",
+ "getNameButDelegateToMethodThatThrows",
+ "shouldStackTraceGetFilteredOnUserExceptionsReflection"));
+ }
+ }
+
// @Test //manual verification
public void verifyTheStackTrace() {
spy.getNameButDelegateToMethodThatThrows();
diff --git a/src/test/java/org/mockitousage/stubbing/StubbingWithAdditionalAnswersTest.java b/src/test/java/org/mockitousage/stubbing/StubbingWithAdditionalAnswersTest.java
index d037cfc0a0..2b96bdf0a9 100644
--- a/src/test/java/org/mockitousage/stubbing/StubbingWithAdditionalAnswersTest.java
+++ b/src/test/java/org/mockitousage/stubbing/StubbingWithAdditionalAnswersTest.java
@@ -56,7 +56,8 @@ public void can_return_arguments_of_invocation() throws Exception {
given(iMethods.objectArgMethod(any())).will(returnsFirstArg());
given(iMethods.threeArgumentMethod(eq(0), any(), anyString())).will(returnsSecondArg());
given(iMethods.threeArgumentMethod(eq(1), any(), anyString())).will(returnsLastArg());
- given(iMethods.mixedVarargsReturningString(eq(1), any())).will(returnsArgAt(2));
+ given(iMethods.mixedVarargsReturningString(eq(1), any(String[].class)))
+ .will(returnsArgAt(2));
assertThat(iMethods.objectArgMethod("first")).isEqualTo("first");
assertThat(iMethods.threeArgumentMethod(0, "second", "whatever")).isEqualTo("second");
@@ -66,8 +67,10 @@ public void can_return_arguments_of_invocation() throws Exception {
@Test
public void can_return_var_arguments_of_invocation() throws Exception {
- given(iMethods.mixedVarargsReturningStringArray(eq(1), any())).will(returnsLastArg());
- given(iMethods.mixedVarargsReturningObjectArray(eq(1), any())).will(returnsArgAt(1));
+ given(iMethods.mixedVarargsReturningStringArray(eq(1), any(String[].class)))
+ .will(returnsLastArg());
+ given(iMethods.mixedVarargsReturningObjectArray(eq(1), any(String[].class)))
+ .will(returnsArgAt(1));
assertThat(iMethods.mixedVarargsReturningStringArray(1, "the", "var", "args"))
.containsExactlyInAnyOrder("the", "var", "args");
@@ -77,7 +80,8 @@ public void can_return_var_arguments_of_invocation() throws Exception {
@Test
public void returns_arg_at_throws_on_out_of_range_var_args() throws Exception {
- given(iMethods.mixedVarargsReturningString(eq(1), any())).will(returnsArgAt(3));
+ given(iMethods.mixedVarargsReturningString(eq(1), any(String[].class)))
+ .will(returnsArgAt(3));
assertThatThrownBy(() -> iMethods.mixedVarargsReturningString(1, "a", "b"))
.isInstanceOf(MockitoException.class)
@@ -111,7 +115,7 @@ public void can_return_after_delay() throws Exception {
@Test
public void can_return_expanded_arguments_of_invocation() throws Exception {
- given(iMethods.varargsObject(eq(1), any())).will(returnsArgAt(3));
+ given(iMethods.varargsObject(eq(1), any(Object[].class))).will(returnsArgAt(3));
assertThat(iMethods.varargsObject(1, "bob", "alexander", "alice", "carl"))
.isEqualTo("alice");
@@ -389,7 +393,7 @@ public void answer(
@Test
public void can_return_based_on_strongly_types_one_parameter_var_args_function()
throws Exception {
- given(iMethods.varargs(any()))
+ given(iMethods.varargs(any(String[].class)))
.will(
answer(
new Answer1() {
@@ -406,7 +410,7 @@ public void will_execute_a_void_based_on_strongly_typed_one_parameter_var_args_f
throws Exception {
final IMethods target = mock(IMethods.class);
- given(iMethods.varargs(any()))
+ given(iMethods.varargs(any(String[].class)))
.will(
answerVoid(
new VoidAnswer1() {
@@ -425,7 +429,7 @@ public void answer(String[] s) {
@Test
public void can_return_based_on_strongly_typed_two_parameter_var_args_function()
throws Exception {
- given(iMethods.mixedVarargsReturningString(any(), any()))
+ given(iMethods.mixedVarargsReturningString(any(), any(String[].class)))
.will(
answer(
new Answer2() {
@@ -442,7 +446,7 @@ public void will_execute_a_void_based_on_strongly_typed_two_parameter_var_args_f
throws Exception {
final IMethods target = mock(IMethods.class);
- given(iMethods.mixedVarargsReturningString(any(), any()))
+ given(iMethods.mixedVarargsReturningString(any(), any(String[].class)))
.will(
answerVoid(
new VoidAnswer2() {
@@ -463,7 +467,7 @@ public void can_return_based_on_strongly_typed_three_parameter_var_args_function
throws Exception {
final IMethods target = mock(IMethods.class);
- given(iMethods.threeArgumentVarArgsMethod(anyInt(), any(), any()))
+ given(iMethods.threeArgumentVarArgsMethod(anyInt(), any(), any(String[].class)))
.will(
answer(
new Answer3() {
@@ -486,7 +490,7 @@ public void will_execute_a_void_based_on_strongly_typed_three_parameter_var_args
throws Exception {
final IMethods target = mock(IMethods.class);
- given(iMethods.threeArgumentVarArgsMethod(anyInt(), any(), any()))
+ given(iMethods.threeArgumentVarArgsMethod(anyInt(), any(), any(String[].class)))
.will(
answerVoid(
new VoidAnswer3() {
@@ -506,7 +510,7 @@ public void answer(Integer i, String s1, String[] s2) {
public void can_return_based_on_strongly_typed_four_parameter_var_args_function()
throws Exception {
final IMethods target = mock(IMethods.class);
- given(iMethods.fourArgumentVarArgsMethod(anyInt(), any(), anyInt(), any()))
+ given(iMethods.fourArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(String[].class)))
.will(
answer(
new Answer4() {
@@ -531,7 +535,7 @@ public void will_execute_a_void_based_on_strongly_typed_four_parameter_var_args_
throws Exception {
final IMethods target = mock(IMethods.class);
- given(iMethods.fourArgumentVarArgsMethod(anyInt(), any(), anyInt(), any()))
+ given(iMethods.fourArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(String[].class)))
.will(
answerVoid(
new VoidAnswer4() {
@@ -552,7 +556,9 @@ public void answer(
public void can_return_based_on_strongly_typed_five_parameter_var_args_function()
throws Exception {
final IMethods target = mock(IMethods.class);
- given(iMethods.fiveArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(), any()))
+ given(
+ iMethods.fiveArgumentVarArgsMethod(
+ anyInt(), any(), anyInt(), any(), any(String[].class)))
.will(
answer(
new Answer5() {
@@ -580,7 +586,9 @@ public void will_execute_a_void_based_on_strongly_typed_five_parameter_var_args_
throws Exception {
final IMethods target = mock(IMethods.class);
- given(iMethods.fiveArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(), any()))
+ given(
+ iMethods.fiveArgumentVarArgsMethod(
+ anyInt(), any(), anyInt(), any(), any(String[].class)))
.will(
answerVoid(
new VoidAnswer5() {
@@ -605,7 +613,9 @@ public void answer(
public void can_return_based_on_strongly_typed_six_parameter_var_args_function()
throws Exception {
final IMethods target = mock(IMethods.class);
- given(iMethods.sixArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(), any(), any()))
+ given(
+ iMethods.sixArgumentVarArgsMethod(
+ anyInt(), any(), anyInt(), any(), any(), any(String[].class)))
.will(
answer(
new Answer6<
@@ -641,7 +651,9 @@ public String answer(
public void will_execute_a_void_returning_strongly_typed_six_parameter_var_args_function()
throws Exception {
final IMethods target = mock(IMethods.class);
- given(iMethods.sixArgumentVarArgsMethod(anyInt(), any(), anyInt(), any(), any(), any()))
+ given(
+ iMethods.sixArgumentVarArgsMethod(
+ anyInt(), any(), anyInt(), any(), any(), any(String[].class)))
.will(
answerVoid(
new VoidAnswer6<
@@ -667,7 +679,7 @@ public void answer(
@Test
public void can_accept_array_supertype_for_strongly_typed_var_args_function() throws Exception {
- given(iMethods.varargs(any()))
+ given(iMethods.varargs(any(String[].class)))
.will(
answer(
new Answer1() {
@@ -681,7 +693,7 @@ public Integer answer(Object[] s) {
@Test
public void can_accept_non_vararg_answer_on_var_args_function() throws Exception {
- given(iMethods.varargs(any()))
+ given(iMethods.varargs(any(String[].class)))
.will(
answer(
new Answer2() {
@@ -695,7 +707,7 @@ public Integer answer(String s1, String s2) {
@Test
public void should_work_with_var_args_with_no_elements() throws Exception {
- given(iMethods.varargs(any()))
+ given(iMethods.varargs(any(String[].class)))
.will(
answer(
new Answer1() {
@@ -709,7 +721,7 @@ public Integer answer(String[] s) {
@Test
public void should_work_with_array_var_args() throws Exception {
- given(iMethods.arrayVarargsMethod(any()))
+ given(iMethods.arrayVarargsMethod(any(String[][].class)))
.will(
answer(
new Answer1() {
diff --git a/src/test/java/org/mockitousage/stubbing/StubbingWithCustomAnswerTest.java b/src/test/java/org/mockitousage/stubbing/StubbingWithCustomAnswerTest.java
index 5591bc47b5..dada3d5710 100644
--- a/src/test/java/org/mockitousage/stubbing/StubbingWithCustomAnswerTest.java
+++ b/src/test/java/org/mockitousage/stubbing/StubbingWithCustomAnswerTest.java
@@ -7,7 +7,6 @@
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
-import java.lang.reflect.Method;
import java.util.Set;
import org.junit.Test;
@@ -110,7 +109,8 @@ public void shouldMakeSureTheInterfaceDoesNotChange() throws Exception {
new Answer() {
public String answer(InvocationOnMock invocation) throws Throwable {
assertTrue(invocation.getArguments().getClass().isArray());
- assertEquals(Method.class, invocation.getMethod().getClass());
+ assertEquals(
+ IMethods.class, invocation.getMethod().getDeclaringClass());
return "assertions passed";
}
diff --git a/src/test/java/org/mockitousage/verification/DescriptiveMessagesWhenVerificationFailsTest.java b/src/test/java/org/mockitousage/verification/DescriptiveMessagesWhenVerificationFailsTest.java
index bae7c31df7..a1a29c1aad 100644
--- a/src/test/java/org/mockitousage/verification/DescriptiveMessagesWhenVerificationFailsTest.java
+++ b/src/test/java/org/mockitousage/verification/DescriptiveMessagesWhenVerificationFailsTest.java
@@ -83,7 +83,7 @@ public void should_print_actual_and_wanted_in_line() {
String actual =
"\n"
- + "Actual invocations have different arguments:"
+ + "Actual invocations have different arguments at position [1]:"
+ "\n"
+ "iMethods.varargs(1, 2);";
@@ -370,37 +370,43 @@ public void should_never_break_method_string_when_no_args_in_method() throws Exc
@Test
public void should_print_fully_qualified_name_when_arguments_classes_have_same_simple_name() {
+ Date dateArg = new Date(0);
+ String stringifiedDateArg = dateArg.toString();
+
try {
- mock.overloadedMethodWithSameClassNameArguments(new Date(0), new IMethods.Date(0));
- verify(mock)
- .overloadedMethodWithSameClassNameArguments(new IMethods.Date(0), new Date(0));
+ mock.overloadedMethodWithSameClassNameArguments(dateArg, new IMethods.Date(0));
+ verify(mock).overloadedMethodWithSameClassNameArguments(new IMethods.Date(0), dateArg);
fail();
} catch (Throwable e) {
String wanted =
- "\n"
- + "Argument(s) are different! Wanted:"
- + "\n"
- + "iMethods.overloadedMethodWithSameClassNameArguments("
- + "\n"
- + " (org.mockitousage.IMethods.Date) 0,"
- + "\n"
- + " (java.sql.Date) 1970-01-01"
- + "\n"
- + ");";
+ String.format(
+ "\n"
+ + "Argument(s) are different! Wanted:"
+ + "\n"
+ + "iMethods.overloadedMethodWithSameClassNameArguments("
+ + "\n"
+ + " (org.mockitousage.IMethods.Date) 0,"
+ + "\n"
+ + " (java.sql.Date) %s"
+ + "\n"
+ + ");",
+ stringifiedDateArg);
assertThat(e).hasMessageContaining(wanted);
String actual =
- "\n"
- + "Actual invocations have different arguments:"
- + "\n"
- + "iMethods.overloadedMethodWithSameClassNameArguments("
- + "\n"
- + " (java.sql.Date) 1970-01-01,"
- + "\n"
- + " (org.mockitousage.IMethods.Date) 0"
- + "\n"
- + ");";
+ String.format(
+ "\n"
+ + "Actual invocations have different arguments at positions [0, 1]:"
+ + "\n"
+ + "iMethods.overloadedMethodWithSameClassNameArguments("
+ + "\n"
+ + " (java.sql.Date) %s,"
+ + "\n"
+ + " (org.mockitousage.IMethods.Date) 0"
+ + "\n"
+ + ");",
+ stringifiedDateArg);
assertThat(e).hasMessageContaining(actual);
}
}
@@ -429,7 +435,7 @@ public void should_print_fully_qualified_name_when_arguments_classes_have_same_s
String actual =
"\n"
- + "Actual invocations have different arguments:"
+ + "Actual invocations have different arguments at positions [0, 1]:"
+ "\n"
+ "iMethods.overloadedMethodWithDifferentClassNameArguments("
+ "\n"
@@ -445,43 +451,50 @@ public void should_print_fully_qualified_name_when_arguments_classes_have_same_s
@Test
public void
should_print_fully_qualified_name_when_some_arguments_classes_have_same_simple_name() {
+ Date dateArg = new Date(0);
+ String stringifiedDateArg = dateArg.toString();
+
try {
mock.overloadedMethodWithSameClassNameArguments(
- new Date(0), "string", new IMethods.Date(0));
+ dateArg, "string", new IMethods.Date(0));
verify(mock)
.overloadedMethodWithSameClassNameArguments(
- new IMethods.Date(0), "string", new Date(0));
+ new IMethods.Date(0), "string", dateArg);
fail();
} catch (Throwable e) {
String wanted =
- "\n"
- + "Argument(s) are different! Wanted:"
- + "\n"
- + "iMethods.overloadedMethodWithSameClassNameArguments("
- + "\n"
- + " (org.mockitousage.IMethods.Date) 0,"
- + "\n"
- + " \"string\","
- + "\n"
- + " (java.sql.Date) 1970-01-01"
- + "\n"
- + ");";
+ String.format(
+ "\n"
+ + "Argument(s) are different! Wanted:"
+ + "\n"
+ + "iMethods.overloadedMethodWithSameClassNameArguments("
+ + "\n"
+ + " (org.mockitousage.IMethods.Date) 0,"
+ + "\n"
+ + " \"string\","
+ + "\n"
+ + " (java.sql.Date) %s"
+ + "\n"
+ + ");",
+ stringifiedDateArg);
assertThat(e).hasMessageContaining(wanted);
String actual =
- "\n"
- + "Actual invocations have different arguments:"
- + "\n"
- + "iMethods.overloadedMethodWithSameClassNameArguments("
- + "\n"
- + " (java.sql.Date) 1970-01-01,"
- + "\n"
- + " \"string\","
- + "\n"
- + " (org.mockitousage.IMethods.Date) 0"
- + "\n"
- + ");";
+ String.format(
+ "\n"
+ + "Actual invocations have different arguments at positions [0, 2]:"
+ + "\n"
+ + "iMethods.overloadedMethodWithSameClassNameArguments("
+ + "\n"
+ + " (java.sql.Date) %s,"
+ + "\n"
+ + " \"string\","
+ + "\n"
+ + " (org.mockitousage.IMethods.Date) 0"
+ + "\n"
+ + ");",
+ stringifiedDateArg);
assertThat(e).hasMessageContaining(actual);
}
}
diff --git a/src/test/java/org/mockitoutil/TestBase.java b/src/test/java/org/mockitoutil/TestBase.java
index 2fe89504e4..3f772d020b 100644
--- a/src/test/java/org/mockitoutil/TestBase.java
+++ b/src/test/java/org/mockitoutil/TestBase.java
@@ -17,7 +17,7 @@
import org.mockito.StateMaster;
import org.mockito.internal.MockitoCore;
import org.mockito.internal.configuration.ConfigurationAccess;
-import org.mockito.internal.debugging.LocationImpl;
+import org.mockito.internal.debugging.LocationFactory;
import org.mockito.internal.invocation.InterceptedInvocation;
import org.mockito.internal.invocation.InvocationBuilder;
import org.mockito.internal.invocation.InvocationMatcher;
@@ -84,7 +84,7 @@ protected static Invocation invocationOf(Class> type, String methodName, Objec
new SerializableMethod(type.getMethod(methodName, types)),
args,
InterceptedInvocation.NO_OP,
- new LocationImpl(),
+ LocationFactory.create(),
1);
}
diff --git a/subprojects/android/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor b/subprojects/android/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor
new file mode 100644
index 0000000000..71111e3378
--- /dev/null
+++ b/subprojects/android/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor
@@ -0,0 +1 @@
+member-accessor-reflection
diff --git a/subprojects/androidTest/androidTest.gradle b/subprojects/androidTest/androidTest.gradle
index b6c32495be..072c222639 100644
--- a/subprojects/androidTest/androidTest.gradle
+++ b/subprojects/androidTest/androidTest.gradle
@@ -6,31 +6,30 @@ plugins {
}
android {
- compileSdkVersion 31
- buildToolsVersion "30.0.3"
+ namespace = "org.mockitousage.androidtest"
+ compileSdk = 33
defaultConfig {
- applicationId "org.mockitousage.androidtest"
- minSdkVersion 21
- targetSdkVersion 31
- versionCode 1
- versionName "1.0"
+ minSdk = 26
+ targetSdk = 33
+ versionCode = 1
+ versionName = "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
- jvmTarget = '1.8'
+ jvmTarget = '11'
+ }
+}
+
+androidComponents {
+ beforeVariants(selector().withBuildType("release")) {
+ enable = false
}
}
@@ -38,16 +37,13 @@ apply from: "$rootDir/gradle/dependencies.gradle"
dependencies {
implementation libraries.kotlin.stdlib
- implementation libraries.android.ktx
- implementation libraries.android.compat
- implementation libraries.android.material
- testImplementation project(":inline")
+ testImplementation project(":")
testImplementation libraries.junit4
testImplementation libraries.junitJupiterApi
testImplementation libraries.junitJupiterEngine
+ androidTestImplementation libraries.android.runner
androidTestImplementation libraries.android.junit
- androidTestImplementation libraries.android.espresso
androidTestImplementation project(":android")
}
diff --git a/subprojects/androidTest/proguard-rules.pro b/subprojects/androidTest/proguard-rules.pro
deleted file mode 100644
index f1b424510d..0000000000
--- a/subprojects/androidTest/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/subprojects/androidTest/src/androidTest/java/org/mockitousage/androidtest/BasicInstrumentedTests.kt b/subprojects/androidTest/src/androidTest/java/org/mockitousage/androidtest/BasicInstrumentedTests.kt
index 2fa04c8bd6..07378171fc 100644
--- a/subprojects/androidTest/src/androidTest/java/org/mockitousage/androidtest/BasicInstrumentedTests.kt
+++ b/subprojects/androidTest/src/androidTest/java/org/mockitousage/androidtest/BasicInstrumentedTests.kt
@@ -35,7 +35,6 @@ class BasicInstrumentedTests {
fun mockAndUseBasicClassUsingAnnotatedMock() {
val basicClass = BasicOpenClassReceiver(mockedViaAnnotationBasicOpenClass)
basicClass.callDependencyMethod()
- throw Exception("java")
}
@Test
diff --git a/subprojects/androidTest/src/main/AndroidManifest.xml b/subprojects/androidTest/src/main/AndroidManifest.xml
index cc2901a874..955375b4e3 100644
--- a/subprojects/androidTest/src/main/AndroidManifest.xml
+++ b/subprojects/androidTest/src/main/AndroidManifest.xml
@@ -1,13 +1,10 @@
-
+
+ android:supportsRtl="true" />
diff --git a/subprojects/androidTest/src/main/res/drawable-v24/ic_launcher_foreground.xml b/subprojects/androidTest/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index 7706ab9e6d..0000000000
--- a/subprojects/androidTest/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/subprojects/androidTest/src/main/res/drawable/ic_launcher_background.xml b/subprojects/androidTest/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 07d5da9cbf..0000000000
--- a/subprojects/androidTest/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d61..0000000000
--- a/subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d61..0000000000
--- a/subprojects/androidTest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/subprojects/androidTest/src/main/res/mipmap-hdpi/ic_launcher.png b/subprojects/androidTest/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a571e60098..0000000000
Binary files a/subprojects/androidTest/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/subprojects/androidTest/src/main/res/mipmap-hdpi/ic_launcher_round.png b/subprojects/androidTest/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 61da551c55..0000000000
Binary files a/subprojects/androidTest/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/subprojects/androidTest/src/main/res/mipmap-mdpi/ic_launcher.png b/subprojects/androidTest/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index c41dd28531..0000000000
Binary files a/subprojects/androidTest/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/subprojects/androidTest/src/main/res/mipmap-mdpi/ic_launcher_round.png b/subprojects/androidTest/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index db5080a752..0000000000
Binary files a/subprojects/androidTest/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/subprojects/androidTest/src/main/res/mipmap-xhdpi/ic_launcher.png b/subprojects/androidTest/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 6dba46dab1..0000000000
Binary files a/subprojects/androidTest/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/subprojects/androidTest/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/subprojects/androidTest/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index da31a871c8..0000000000
Binary files a/subprojects/androidTest/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/subprojects/androidTest/src/main/res/mipmap-xxhdpi/ic_launcher.png b/subprojects/androidTest/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 15ac681720..0000000000
Binary files a/subprojects/androidTest/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/subprojects/androidTest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/subprojects/androidTest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index b216f2d313..0000000000
Binary files a/subprojects/androidTest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/subprojects/androidTest/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/subprojects/androidTest/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index f25a419744..0000000000
Binary files a/subprojects/androidTest/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/subprojects/androidTest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/subprojects/androidTest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index e96783ccce..0000000000
Binary files a/subprojects/androidTest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/subprojects/androidTest/src/main/res/values-night/themes.xml b/subprojects/androidTest/src/main/res/values-night/themes.xml
deleted file mode 100644
index 70198506d0..0000000000
--- a/subprojects/androidTest/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
diff --git a/subprojects/androidTest/src/main/res/values/colors.xml b/subprojects/androidTest/src/main/res/values/colors.xml
deleted file mode 100644
index ca1931bca9..0000000000
--- a/subprojects/androidTest/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- #FFBB86FC
- #FF6200EE
- #FF3700B3
- #FF03DAC5
- #FF018786
- #FF000000
- #FFFFFFFF
-
diff --git a/subprojects/androidTest/src/main/res/values/strings.xml b/subprojects/androidTest/src/main/res/values/strings.xml
index 9aab1293ac..33d20491a8 100644
--- a/subprojects/androidTest/src/main/res/values/strings.xml
+++ b/subprojects/androidTest/src/main/res/values/strings.xml
@@ -1,3 +1,9 @@
+
+
+
Mockito Android Tests
diff --git a/subprojects/androidTest/src/main/res/values/themes.xml b/subprojects/androidTest/src/main/res/values/themes.xml
deleted file mode 100644
index d21572ebb6..0000000000
--- a/subprojects/androidTest/src/main/res/values/themes.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
diff --git a/subprojects/errorprone/errorprone.gradle b/subprojects/errorprone/errorprone.gradle
index e3ef7a7b77..effbb91d2c 100644
--- a/subprojects/errorprone/errorprone.gradle
+++ b/subprojects/errorprone/errorprone.gradle
@@ -10,14 +10,30 @@ dependencies {
implementation project.rootProject
implementation libraries.errorprone
- testImplementation 'junit:junit:4.13-beta-1'
+ testImplementation 'junit:junit:4.13.2'
testImplementation libraries.errorproneTestApi
}
test {
inputs.files(configurations.errorproneJavac).withNormalizer(ClasspathNormalizer)
- jvmArgs += "-Xbootclasspath/p:${configurations.errorproneJavac.asPath}"
+ jvmArgs += "-Xbootclasspath/a:${configurations.errorproneJavac.asPath}"
+ jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"
+ jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.type=ALL-UNNAMED"
+ jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED"
+ jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"
+ jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED"
+ jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED"
+ jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED"
+ jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED"
+ jvmArgs += "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED"
+}
+
+tasks.withType(JavaCompile) {
+ options.compilerArgs << "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED"
+ options.compilerArgs << "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED"
+ options.compilerArgs << "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"
+}
- // ErrorProne can only run on JDK 8
- it.enabled = !JavaVersion.current().isJava9Compatible()
+tasks.withType(Javadoc) {
+ options.addBooleanOption("-add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", true)
}
diff --git a/subprojects/extTest/src/test/java/org/mockitousage/plugins/switcher/MyPluginSwitch.java b/subprojects/extTest/src/test/java/org/mockitousage/plugins/switcher/MyPluginSwitch.java
index eb4423f361..cf60ee63ec 100644
--- a/subprojects/extTest/src/test/java/org/mockitousage/plugins/switcher/MyPluginSwitch.java
+++ b/subprojects/extTest/src/test/java/org/mockitousage/plugins/switcher/MyPluginSwitch.java
@@ -14,6 +14,10 @@ public class MyPluginSwitch implements PluginSwitch {
static List invokedFor = new LinkedList();
public boolean isEnabled(String pluginClassName) {
+ if (!pluginClassName.startsWith("org.mockitousage")) {
+ return false;
+ }
+
invokedFor.add(pluginClassName);
return true;
}
diff --git a/subprojects/groovyInlineTest/groovyInlineTest.gradle b/subprojects/groovyInlineTest/groovyInlineTest.gradle
index 1618ac6009..16e95a9118 100644
--- a/subprojects/groovyInlineTest/groovyInlineTest.gradle
+++ b/subprojects/groovyInlineTest/groovyInlineTest.gradle
@@ -5,7 +5,7 @@ description = "Integration test for using mockito-inline with Groovy."
apply from: "$rootDir/gradle/dependencies.gradle"
dependencies {
- testImplementation project(":inline")
+ testImplementation project(":")
testImplementation libraries.groovy
testImplementation libraries.junit4
}
diff --git a/subprojects/inline/inline.gradle b/subprojects/inline/inline.gradle
deleted file mode 100644
index f96cffce9f..0000000000
--- a/subprojects/inline/inline.gradle
+++ /dev/null
@@ -1,21 +0,0 @@
-description = "Mockito preconfigured inline mock maker (intermediate and to be superseeded by automatic usage in a future version)"
-
-apply from: "$rootDir/gradle/java-library.gradle"
-
-dependencies {
- api project.rootProject
- testImplementation libraries.junit4
- testImplementation libraries.assertj
-}
-
-tasks.javadoc.enabled = false
-
-//required by the "StressTest.java" and "OneLinerStubStressTest.java"
-test.maxHeapSize = "256m"
-retryTest.maxHeapSize = "256m"
-
-if (JavaVersion.current().java9Compatible) {
- test {
- jvmArgs '--illegal-access=deny'
- }
-}
diff --git a/subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker b/subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker
deleted file mode 100644
index ca6ee9cea8..0000000000
--- a/subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker
+++ /dev/null
@@ -1 +0,0 @@
-mock-maker-inline
\ No newline at end of file
diff --git a/subprojects/inlineTest/inlineTest.gradle b/subprojects/inlineTest/inlineTest.gradle
new file mode 100644
index 0000000000..4cc6726c5a
--- /dev/null
+++ b/subprojects/inlineTest/inlineTest.gradle
@@ -0,0 +1,24 @@
+plugins {
+ id 'java'
+}
+
+description = "Mockito preconfigured inline mock maker (intermediate and to be superseeded by automatic usage in a future version)"
+
+apply from: "$rootDir/gradle/dependencies.gradle"
+
+sourceCompatibility = 11
+targetCompatibility = 11
+
+dependencies {
+ implementation project.rootProject
+ testImplementation libraries.junit4
+ testImplementation libraries.assertj
+}
+
+tasks.javadoc.enabled = false
+
+test {
+ jvmArgs '--illegal-access=deny'
+ //required by the "StressTest.java" and "OneLinerStubStressTest.java"
+ maxHeapSize '256m'
+}
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockRuleTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/ConstructionMockRuleTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockRuleTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/ConstructionMockRuleTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/ConstructionMockTest.java
similarity index 90%
rename from subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/ConstructionMockTest.java
index 7eb87c5702..adbe3fe3ee 100644
--- a/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java
+++ b/subprojects/inlineTest/src/test/java/org/mockitoinline/ConstructionMockTest.java
@@ -11,11 +11,13 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
+import org.mockito.MockMakers;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;
import org.mockito.exceptions.base.MockitoException;
@@ -154,6 +156,21 @@ public void testConstructionMockMustNotTargetAbstractClass() {
.hasMessageContaining("It is not possible to construct primitive types or abstract types");
}
+ @Test
+ public void testConstructionMocksMustNotUseCustomMockMaker() {
+ assertThatThrownBy(
+ () -> {
+ try (MockedConstruction ignored = Mockito.mockConstruction(
+ Dummy.class,
+ withSettings().mockMaker(MockMakers.INLINE))
+ ) {
+ new Dummy();
+ }
+ })
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("you cannot override the MockMaker for construction mocks");
+ }
+
static class Dummy {
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/EnumMockingTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/EnumMockingTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/EnumMockingTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/EnumMockingTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/FinalClassMockingTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/FinalClassMockingTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/FinalClassMockingTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/FinalClassMockingTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/HierarchyPreInitializationTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/HierarchyPreInitializationTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/HierarchyPreInitializationTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/HierarchyPreInitializationTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/InOrderVerificationTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/InOrderVerificationTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/InitializationTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/InitializationTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/InitializationTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/InitializationTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/OneLinerStubStressTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/OneLinerStubStressTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/OneLinerStubStressTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/OneLinerStubStressTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/PluginTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/PluginTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/PluginTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/PluginTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/RecursionTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/RecursionTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/RecursionTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/RecursionTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/SpyWithConstructorTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/SpyWithConstructorTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/SpyWithConstructorTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/SpyWithConstructorTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StaticMockRuleTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/StaticMockRuleTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/StaticMockRuleTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/StaticMockRuleTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StaticMockTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/StaticMockTest.java
similarity index 93%
rename from subprojects/inline/src/test/java/org/mockitoinline/StaticMockTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/StaticMockTest.java
index eaa3ed1970..c9f7a58cce 100644
--- a/subprojects/inline/src/test/java/org/mockitoinline/StaticMockTest.java
+++ b/subprojects/inlineTest/src/test/java/org/mockitoinline/StaticMockTest.java
@@ -212,6 +212,15 @@ public void testStaticMockMustUseValidMatchers() {
}
}
+ @Test
+ public void testStaticMockVarargs() {
+ assertEquals("foobar", Dummy.fooVarargs("foo", "bar"));
+ try (MockedStatic ignored = Mockito.mockStatic(Dummy.class)) {
+ assertNull(Dummy.fooVarargs("foo", "bar"));
+ }
+ assertEquals("foobar", Dummy.fooVarargs("foo", "bar"));
+ }
+
static class Dummy {
static String var1 = null;
@@ -227,5 +236,13 @@ static void fooVoid(String var2) {
static void fooVoid(String var2, String var3) {
var1 = var2;
}
+
+ static String fooVarargs(String... args) {
+ StringBuilder sb = new StringBuilder();
+ for (String arg : args) {
+ sb.append(arg);
+ }
+ return sb.toString();
+ }
}
}
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StaticRuleTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/StaticRuleTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/StaticRuleTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/StaticRuleTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StaticRunnerTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/StaticRunnerTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/StaticRunnerTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/StaticRunnerTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StressTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/StressTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/StressTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/StressTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StubbingLocationTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/StubbingLocationTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/StubbingLocationTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/StubbingLocationTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/SubconstructorMockTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/SubconstructorMockTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/SubconstructorMockTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/SubconstructorMockTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/SuperCallTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/SuperCallTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/SuperCallTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/SuperCallTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/bugs/CyclicMockMethodArgumentMemoryLeakTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/bugs/CyclicMockMethodArgumentMemoryLeakTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/bugs/CyclicMockMethodArgumentMemoryLeakTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/bugs/CyclicMockMethodArgumentMemoryLeakTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/bugs/SelfSpyReferenceMemoryLeakTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/bugs/SelfSpyReferenceMemoryLeakTest.java
similarity index 100%
rename from subprojects/inline/src/test/java/org/mockitoinline/bugs/SelfSpyReferenceMemoryLeakTest.java
rename to subprojects/inlineTest/src/test/java/org/mockitoinline/bugs/SelfSpyReferenceMemoryLeakTest.java
diff --git a/subprojects/junit-jupiter/junit-jupiter.gradle b/subprojects/junit-jupiter/junit-jupiter.gradle
index 99f63e75d5..cbd25d34ab 100644
--- a/subprojects/junit-jupiter/junit-jupiter.gradle
+++ b/subprojects/junit-jupiter/junit-jupiter.gradle
@@ -25,7 +25,7 @@ jar {
'Bundle-SymbolicName': 'org.mockito.junit-jupiter',
'Bundle-Version': "\${version_cleanup;${project.version}}",
'-versionpolicy': '[${version;==;${@}},${version;+;${@}})',
- 'Export-Package': "org.mockito.junit.jupiter.*;version=${version}",
+ 'Export-Package': "org.mockito.junit.jupiter.*;version=${archiveVersion.get()}",
'-removeheaders': 'Private-Package',
'Automatic-Module-Name': 'org.mockito.junit.jupiter',
'-noextraheaders': 'true',
diff --git a/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockMultipleMatchesTest.java b/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockMultipleMatchesTest.java
new file mode 100644
index 0000000000..323008eb63
--- /dev/null
+++ b/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockMultipleMatchesTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2023 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package org.mockitousage;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Verify that a {@link MockitoException} is thrown when there are multiple {@link Mock} fields that
+ * do match a candidate field by type, but cannot be matched by name.
+ *
+ * Uses a JUnit 5 extension to obtain the JUnit 5 {@link ExtensionContext} and
+ * pass it to {@link MockitoExtension#beforeEach(ExtensionContext)}, as the exception
+ * is thrown during {@link org.junit.jupiter.api.BeforeEach}.
+ */
+@ExtendWith(GenericTypeMockMultipleMatchesTest.ContextProvidingExtension.class)
+public class GenericTypeMockMultipleMatchesTest {
+
+ private static ExtensionContext currentExtensionContext;
+
+ public static class ContextProvidingExtension implements BeforeEachCallback {
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ currentExtensionContext = context;
+ }
+ }
+
+ private void startMocking(Object testInstance) {
+ MockitoExtension mockitoExtension = new MockitoExtension();
+ mockitoExtension.beforeEach(currentExtensionContext);
+ }
+
+ @Nested
+ public class MultipleCandidatesByTypeTest {
+ public class UnderTestWithMultipleCandidatesByType {
+ List stringList;
+ }
+
+ @Mock
+ List stringList1;
+
+ @Mock
+ List stringList2;
+
+ @InjectMocks
+ UnderTestWithMultipleCandidatesByType underTestWithMultipleCandidates = new UnderTestWithMultipleCandidatesByType();
+
+ @Test
+ void testMultipleCandidatesByTypes() {
+ assertThrows(MockitoException.class, () -> startMocking(this));
+ }
+ }
+
+
+}
diff --git a/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockTest.java b/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockTest.java
new file mode 100644
index 0000000000..4decb2e2e2
--- /dev/null
+++ b/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockTest.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2023 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package org.mockitousage;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.MockitoAnnotations.*;
+
+import java.sql.Time;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests that verify Mockito can discern mocks by generic types, so if there are multiple mock candidates
+ * with the same generic type but different type parameters available for injection into a given field,
+ * Mockito won't fail to inject (even if mock field name doesn't match under test's field name).
+ */
+@ExtendWith(MockitoExtension.class)
+public class GenericTypeMockTest {
+
+ @Nested
+ public class SingleTypeParamTest {
+ public class UnderTestWithSingleTypeParam {
+ List stringList;
+ List intList;
+ }
+
+ @Mock
+ private List stringListMock;
+
+ @Mock
+ private List intListMock;
+
+ // must construct non-static inner class ourselves here
+ // (making it public static classes doesn't work either)
+ @InjectMocks
+ private UnderTestWithSingleTypeParam underTestWithSingleTypeParam = new UnderTestWithSingleTypeParam();
+
+ @Test
+ void testSingleTypeParam() {
+ // testing for not null first before testing for equals,
+ // because assertEquals(null, null) == true,
+ // so test would succeed if both @Mock and @InjectMocks
+ // don't work at all
+ assertNotNull(stringListMock);
+ assertNotNull(intListMock);
+
+ assertEquals(stringListMock, underTestWithSingleTypeParam.stringList);
+ assertEquals(intListMock, underTestWithSingleTypeParam.intList);
+ }
+ }
+
+ @Nested
+ public class WildcardTest {
+ class UnderTestWithWildcard {
+ Set extends Date> dateSet;
+ Set extends Number> numberSet;
+ }
+
+ @Mock
+ Set timeSetMock; // java.sql.Time extends Date
+
+ @Mock
+ Set integerSetMock;
+
+ @InjectMocks
+ UnderTestWithWildcard underTestWithWildcard = new UnderTestWithWildcard();
+
+ @Test
+ void testWildcard() {
+ assertNotNull(timeSetMock);
+ assertNotNull(integerSetMock);
+
+ // this also tests whether WildcardType.upperBounds() is evaluated,
+ // i.e. that we match extends Date> to type parameter
+ assertEquals(timeSetMock, underTestWithWildcard.dateSet);
+ assertEquals(integerSetMock, underTestWithWildcard.numberSet);
+ }
+ }
+
+
+ @Nested
+ public class NestedTypeParametersTest {
+ public class UnderTestWithNestedTypeParameters {
+ Map> intToStringCollectionMap;
+ Map> intoToIntCollectionMap;
+ }
+
+ @Mock
+ Map> intToStringCollectionMapMock;
+
+ @Mock
+ Map> intToIntCollectionMapMock;
+
+ @InjectMocks
+ UnderTestWithNestedTypeParameters underTestWithNestedTypeParameters = new UnderTestWithNestedTypeParameters();
+
+ @Test
+ void testNestedTypeParameters() {
+ assertNotNull(intToStringCollectionMapMock);
+ assertNotNull(intToIntCollectionMapMock);
+
+ assertEquals(intToStringCollectionMapMock, underTestWithNestedTypeParameters.intToStringCollectionMap);
+ assertEquals(intToIntCollectionMapMock, underTestWithNestedTypeParameters.intoToIntCollectionMap);
+ }
+ }
+
+ @Nested
+ public class GenericSubclassTest {
+ public class UnderTestWithGenericSubclass {
+ Set stringSet;
+ Set intSet;
+ }
+
+ @Mock
+ TreeSet stringTreeSetMock;
+
+ @Mock
+ HashSet intHashSetMock;
+
+ @InjectMocks
+ UnderTestWithGenericSubclass underTestWithGenericSubclass = new UnderTestWithGenericSubclass();
+
+ @Test
+ void testGenericSubclass() {
+ assertNotNull(stringTreeSetMock);
+ assertNotNull(intHashSetMock);
+
+ assertEquals(stringTreeSetMock, underTestWithGenericSubclass.stringSet);
+ assertEquals(intHashSetMock, underTestWithGenericSubclass.intSet);
+ }
+ }
+
+ @Nested
+ public class MultipleCandidatesOneByNameTest {
+ public class UnderTestWithMultipleCandidatesOneByName {
+ List stringList;
+ }
+
+ @Mock
+ List stringList;
+
+ @Mock
+ List stringListMock;
+
+ @InjectMocks
+ UnderTestWithMultipleCandidatesOneByName underTestWithMultipleCandidatesOneByName = new UnderTestWithMultipleCandidatesOneByName();
+
+ @Test
+ void testMultipleCandidatesOneByName() {
+ // verify that when multiple mock candidates exist by type, and one of them matches by field name, that one is injected
+ assertNotNull(underTestWithMultipleCandidatesOneByName.stringList);
+ assertEquals(stringList, underTestWithMultipleCandidatesOneByName.stringList);
+ }
+ }
+
+ @Nested
+ public class NoneMatchByTypeParameterTest {
+ public class UnderTestWithNoMatches {
+ List intList;
+ }
+
+ @Mock
+ List stringList;
+
+ @InjectMocks
+ UnderTestWithNoMatches underTestWithNoMatches = new UnderTestWithNoMatches();
+
+ @Test
+ void testNoneMatchByTypeParameter() {
+ assertNotNull(stringList);
+
+ // verify that when no candidate matches by type parameter, none is injected
+ assertNull(underTestWithNoMatches.intList);
+ }
+ }
+
+ @Nested
+ public class NoneMatchByRawTypeTest {
+ public class UnderTestWithNoMatches {
+ List intList;
+ }
+
+ @Mock
+ Set intSet;
+
+ @InjectMocks
+ UnderTestWithNoMatches underTestWithNoMatchesByRawType = new UnderTestWithNoMatches();
+
+ @Test
+ void testNoneMatchByRawType() {
+ assertNotNull(intSet);
+
+ // verify that when no candidate matches by raw type, none is injected
+ assertNull(underTestWithNoMatchesByRawType.intList);
+ }
+ }
+
+
+ @Nested
+ public class ClassWithTypeParameterNoMatchTest {
+ public class UnderTestWithTypeParameter {
+ List tList;
+ }
+
+ @Mock
+ List intList;
+
+ @InjectMocks
+ UnderTestWithTypeParameter underTestWithTypeParameterNoMatch = new UnderTestWithTypeParameter();
+
+ @Test
+ void testWithTypeParameterNoMatch() {
+ assertNotNull(intList);
+
+ // verify that when no candidate matches by type parameter of class under test, none is injected
+ assertNull(underTestWithTypeParameterNoMatch.tList);
+ }
+ }
+
+ @Nested
+ public class ClassWithTypeParametersTest {
+ public class UnderTestWithTypeParameters {
+ List t1List;
+ List t2List;
+ }
+
+ @Mock
+ List stringList;
+
+ @Mock
+ List intList;
+
+ @InjectMocks
+ UnderTestWithTypeParameters underTestWithTypeParameters = new UnderTestWithTypeParameters();
+
+ @Test
+ void testWithTypeParameters() {
+ assertNotNull(stringList);
+ assertNotNull(intList);
+
+ // verify that we can match the type parameters of the class under test
+ assertEquals(stringList, underTestWithTypeParameters.t1List);
+ assertEquals(intList, underTestWithTypeParameters.t2List);
+ }
+ }
+
+ @Nested
+ public class InjectConcreteClassInFieldWithTypeParameter {
+ public class UnderTestWithTypeParameter {
+ List tList;
+ }
+
+ public class ConcreteStringList extends ArrayList {}
+
+ @Mock
+ ConcreteStringList concreteStringList;
+
+ @InjectMocks
+ UnderTestWithTypeParameter underTestWithTypeParameters = new UnderTestWithTypeParameter();
+
+ @Test
+ void testWithTypeParameters() {
+ assertNotNull(concreteStringList);
+
+ // verify that we can match the type parameters of the class under test
+ assertEquals(concreteStringList, underTestWithTypeParameters.tList);
+ }
+ }
+
+ @Nested
+ public class NoneMatchInjectConcreteClassInFieldWithTypeParameterTest {
+ public class UnderTestWithTypeParameter {
+ List tList;
+ }
+
+ public class ConcreteStringList extends ArrayList {}
+
+ @Mock
+ ConcreteStringList concreteStringList;
+
+ @InjectMocks
+ UnderTestWithTypeParameter underTestWithTypeParameters = new UnderTestWithTypeParameter();
+
+ @Test
+ void testWithTypeParameters() {
+ assertNotNull(concreteStringList);
+
+ // verify that when no concrete type candidate matches, none is injected
+ assertNull(underTestWithTypeParameters.tList);
+ }
+ }
+
+ /**
+ * Verify regression https://github.com/mockito/mockito/issues/2958 is fixed.
+ */
+ @Nested
+ public class RegressionClassCastException {
+ public class AbstractUnderTest> {
+ UnderTestInstance instance;
+ }
+
+ public class UnderTestInstance> {
+ }
+
+ public class ConcreteUnderTest extends AbstractUnderTest {
+ }
+
+ @Mock
+ UnderTestInstance instanceMock;
+
+ @InjectMocks
+ protected ConcreteUnderTest concreteUnderTest = new ConcreteUnderTest();
+
+ @BeforeEach
+ public void initMocks()
+ {
+ openMocks(this);
+ }
+
+ @Test
+ public void testMockExists() {
+ assertNotNull(instanceMock);
+ assertEquals(instanceMock, concreteUnderTest.instance);
+ }
+
+
+ }
+
+}
+
diff --git a/subprojects/kotlinReleaseCoroutinesTest/kotlinReleaseCoroutinesTest.gradle b/subprojects/kotlinReleaseCoroutinesTest/kotlinReleaseCoroutinesTest.gradle
index a8512591ce..284b3bdfb7 100644
--- a/subprojects/kotlinReleaseCoroutinesTest/kotlinReleaseCoroutinesTest.gradle
+++ b/subprojects/kotlinReleaseCoroutinesTest/kotlinReleaseCoroutinesTest.gradle
@@ -1,3 +1,6 @@
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
buildscript {
repositories {
mavenCentral()
@@ -17,6 +20,15 @@ repositories {
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
}
+sourceCompatibility = 11
+targetCompatibility = 11
+
+tasks.withType(KotlinCompile).configureEach {
+ compilerOptions {
+ jvmTarget = JvmTarget.JVM_11
+ }
+}
+
dependencies {
testImplementation project(":")
testImplementation libraries.junit4
diff --git a/subprojects/kotlinTest/kotlinTest.gradle b/subprojects/kotlinTest/kotlinTest.gradle
index 55c8e8036f..99397def9f 100644
--- a/subprojects/kotlinTest/kotlinTest.gradle
+++ b/subprojects/kotlinTest/kotlinTest.gradle
@@ -1,3 +1,6 @@
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'java'
@@ -7,6 +10,15 @@ description = "Kotlin tests for Mockito."
apply from: "$rootDir/gradle/dependencies.gradle"
+sourceCompatibility = 11
+targetCompatibility = 11
+
+tasks.withType(KotlinCompile).configureEach {
+ compilerOptions {
+ jvmTarget = JvmTarget.JVM_11
+ }
+}
+
dependencies {
testImplementation project(":")
testImplementation libraries.junit4
diff --git a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/CircularityTest.kt b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/CircularityTest.kt
index 82fccea672..cd499897be 100644
--- a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/CircularityTest.kt
+++ b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/CircularityTest.kt
@@ -1,3 +1,7 @@
+/**
+ * Copyright (c) 2017 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
package org.mockito.kotlin
import org.junit.Before
diff --git a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt
index a1a64ce0b0..8100b1282d 100644
--- a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt
+++ b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt
@@ -1,3 +1,7 @@
+/**
+ * Copyright (c) 2017 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
package org.mockito.kotlin
import org.junit.Assert.assertEquals
@@ -339,4 +343,14 @@ class InlineClassTest {
verify(mock).returnsResult()
}
+
+ @Test
+ @SuppressWarnings("DoNotMock", "DoNotMockAutoValue")
+ fun automaticallyDetectsClassToMock() {
+ val mock: WithResult = mock()
+
+ `when`(mock.returnsResult()).thenReturn(Result.success("OK"))
+
+ assertEquals("OK", mock.returnsResult().getOrNull())
+ }
}
diff --git a/subprojects/memory-test/src/test/java/org/mockito/memorytest/LocationFactoryAllocationRateTest.java b/subprojects/memory-test/src/test/java/org/mockito/memorytest/LocationFactoryAllocationRateTest.java
new file mode 100644
index 0000000000..26b36ca9af
--- /dev/null
+++ b/subprojects/memory-test/src/test/java/org/mockito/memorytest/LocationFactoryAllocationRateTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2022 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.memorytest;
+
+import com.sun.management.ThreadMXBean;
+import org.junit.Test;
+import org.mockito.internal.debugging.LocationFactory;
+
+import java.lang.management.ManagementFactory;
+import java.util.stream.IntStream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LocationFactoryAllocationRateTest {
+ private static final int REPEAT = 1000;
+ private static final int RECURSION_LIMIT = 1000;
+ private static final double EXPECTED_IMPROVEMENT = expectedImprovement();
+
+ private static final ThreadMXBean memoryBean =
+ (ThreadMXBean) ManagementFactory.getThreadMXBean();
+
+
+ @Test
+ public void shouldAllocateMuchLessMemoryThanThrowable() {
+ // On Java 8, this will use the internal approach. On Java 9, the StackWalker approach will
+ // be used.
+ new Throwable().fillInStackTrace();
+ LocationFactory.create();
+ long baseline =
+ countMemoryAllocations(
+ () ->
+ recurseAndThen(
+ RECURSION_LIMIT,
+ repeat(() -> new Throwable().fillInStackTrace())));
+ long actual =
+ countMemoryAllocations(
+ () ->
+ recurseAndThen(
+ RECURSION_LIMIT,
+ repeat(() -> LocationFactory.create(false))));
+ assertThat(actual * EXPECTED_IMPROVEMENT)
+ .as(
+ "stack walker approach (%d) expected to be at least %fx better than exception approach (%d)",
+ actual, EXPECTED_IMPROVEMENT, baseline)
+ .isLessThan(baseline);
+ }
+
+ private static long countMemoryAllocations(Runnable someTask) {
+ long threadId = Thread.currentThread().getId();
+ long atStart = memoryBean.getThreadAllocatedBytes(threadId);
+ someTask.run();
+ return memoryBean.getThreadAllocatedBytes(threadId) - atStart;
+ }
+
+ private static void recurseAndThen(int count, Runnable runnable) {
+ if (count <= 0) {
+ runnable.run();
+ } else {
+ recurseAndThen(count - 1, runnable);
+ }
+ }
+
+ private static Runnable repeat(Runnable task) {
+ return () -> IntStream.range(0, REPEAT).forEach(index -> task.run());
+ }
+
+ private static double expectedImprovement() {
+ try {
+ Class.forName("java.lang.StackWalker");
+ return 20;
+ } catch (ClassNotFoundException e) {
+ return 1.5;
+ }
+ }
+}
diff --git a/subprojects/memory-test/src/test/java/org/mockito/memorytest/ShouldNotStarveMemoryOnLargeStackTraceInvocationsTest.java b/subprojects/memory-test/src/test/java/org/mockito/memorytest/ShouldNotStarveMemoryOnLargeStackTraceInvocationsTest.java
index d3a6903e21..5c8bac2630 100644
--- a/subprojects/memory-test/src/test/java/org/mockito/memorytest/ShouldNotStarveMemoryOnLargeStackTraceInvocationsTest.java
+++ b/subprojects/memory-test/src/test/java/org/mockito/memorytest/ShouldNotStarveMemoryOnLargeStackTraceInvocationsTest.java
@@ -10,10 +10,8 @@
import static org.mockito.Mockito.when;
import org.junit.Assume;
-import org.junit.Ignore;
import org.junit.Test;
-@Ignore("https://github.com/mockito/mockito/issues/2478")
public class ShouldNotStarveMemoryOnLargeStackTraceInvocationsTest {
private static final int STACK_TRACE_DEPTH = 1000;
@@ -23,12 +21,7 @@ public class ShouldNotStarveMemoryOnLargeStackTraceInvocationsTest {
static {
try {
- Class.forName("sun.misc.SharedSecrets")
- .getMethod("getJavaLangAccess")
- .invoke(null);
- Class.forName("sun.misc.JavaLangAccess")
- .getMethod("getStackTraceElement", Throwable.class, int.class);
-
+ Class.forName("java.lang.StackWalker");
supported = true;
} catch (Exception ignored) {
}
diff --git a/subprojects/module-test/module-test.gradle b/subprojects/module-test/module-test.gradle
index 3be8730cc6..d38a32c7d1 100644
--- a/subprojects/module-test/module-test.gradle
+++ b/subprojects/module-test/module-test.gradle
@@ -2,10 +2,6 @@ plugins {
id 'java'
}
-if (JavaVersion.current() == JavaVersion.VERSION_1_8) {
- project.tasks.all { task -> task.enabled = false }
-}
-
description = "Test suite for Java 9 modules with Mockito"
apply from: "$rootDir/gradle/dependencies.gradle"
@@ -18,5 +14,5 @@ dependencies {
tasks.javadoc.enabled = false
-sourceCompatibility = 1.9
-targetCompatibility = 1.9
+sourceCompatibility = 11
+targetCompatibility = 11
diff --git a/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleAccessTest.java b/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleAccessTest.java
index 55923e77b5..460dc906f0 100644
--- a/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleAccessTest.java
+++ b/subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleAccessTest.java
@@ -4,9 +4,11 @@
*/
package org.mockito.moduletest;
+import org.junit.Assume;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.internal.util.reflection.ModuleMemberAccessor;
import org.mockito.internal.util.reflection.ReflectionMemberAccessor;
@@ -17,6 +19,8 @@
import static junit.framework.TestCase.fail;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.not;
import static org.mockito.moduletest.ModuleUtil.layer;
import static org.mockito.moduletest.ModuleUtil.modularJar;
@@ -73,6 +77,8 @@ public void cannot_access_non_opened_module_with_reflection_member_accessor() th
@Test
public void cannot_read_unopened_private_field_but_exception_includes_cause() throws Exception {
+ Assume.assumeThat(Plugins.getMemberAccessor(), not(instanceOf(ModuleMemberAccessor.class)));
+
Path jar = modularJar(true, true, false, true);
ModuleLayer layer = layer(jar, true, true);
@@ -91,4 +97,21 @@ public void cannot_read_unopened_private_field_but_exception_includes_cause() th
}
}
+ @Test
+ public void can_read_unopened_private_field_but_exception_includes_cause() throws Exception {
+ Assume.assumeThat(Plugins.getMemberAccessor(), instanceOf(ModuleMemberAccessor.class));
+
+ Path jar = modularJar(true, true, false, true);
+ ModuleLayer layer = layer(jar, true, true);
+
+ ClassLoader loader = layer.findLoader("mockito.test");
+ Class> type = loader.loadClass("sample.MyCallable");
+
+ @SuppressWarnings("unchecked")
+ Callable testInstance = (Callable) type.getDeclaredConstructor().newInstance();
+
+ Mockito.mockitoSession()
+ .initMocks(testInstance)
+ .startMocking();
+ }
}
diff --git a/subprojects/module-test/src/test/java/org/mockito/moduletest/ReplicatingClassLoader.java b/subprojects/module-test/src/test/java/org/mockito/moduletest/ReplicatingClassLoader.java
index c9cf0a32d2..144ff98767 100644
--- a/subprojects/module-test/src/test/java/org/mockito/moduletest/ReplicatingClassLoader.java
+++ b/subprojects/module-test/src/test/java/org/mockito/moduletest/ReplicatingClassLoader.java
@@ -53,4 +53,9 @@ public Class> loadClass(String name) throws ClassNotFoundException {
protected URL findResource(String moduleName, String name) {
return Mockito.class.getResource("/" + name);
}
+
+ @Override
+ public InputStream getResourceAsStream(String name) {
+ return Mockito.class.getResourceAsStream("/" + name);
+ }
}
diff --git a/subprojects/osgi-test/osgi-test.gradle b/subprojects/osgi-test/osgi-test.gradle
index 3689333d1e..0861fd7f46 100644
--- a/subprojects/osgi-test/osgi-test.gradle
+++ b/subprojects/osgi-test/osgi-test.gradle
@@ -8,6 +8,7 @@ apply from: "osgi-test-bundles.gradle"
description = "Test suite for OSGi framework with Mockito"
dependencies {
+ testImplementation project.rootProject
testImplementation libraries.junit4
testImplementation libraries.osgi
@@ -29,10 +30,31 @@ dependencies {
}
test {
+ jvmArgumentProviders.add(
+ new RuntimeBundlesProvider(files: configurations.testRuntimeBundles.asFileTree)
+ )
dependsOn configurations.testRuntimeBundles
inputs.files(sourceSets.testBundle.allSource)
+ .withPathSensitivity(PathSensitivity.RELATIVE)
+ .withPropertyName('testBundleSources')
inputs.files(sourceSets.otherBundle.allSource)
- systemProperty 'testRuntimeBundles', configurations.testRuntimeBundles.asPath
+ .withPathSensitivity(PathSensitivity.RELATIVE)
+ .withPropertyName('otherBundleSources')
useJUnit()
}
+/**
+ * A helper class to pass classpath elements as relative paths. This allows the build
+ * to be checked out in different locations on the file system and still hit the cache.
+ */
+class RuntimeBundlesProvider implements CommandLineArgumentProvider {
+ @InputFiles
+ @PathSensitive(PathSensitivity.RELATIVE)
+ FileTree files
+
+ @Override
+ Iterable asArguments() {
+ String[] absolutePaths = files.stream().map {it.absolutePath}.toArray()
+ ["-DtestRuntimeBundles=${CollectionUtils.join(File.pathSeparator, absolutePaths)}".toString()]
+ }
+}
diff --git a/subprojects/osgi-test/src/test/java/org/mockito/osgitest/OsgiTest.java b/subprojects/osgi-test/src/test/java/org/mockito/osgitest/OsgiTest.java
index 296e6cfd7f..00d12234bb 100644
--- a/subprojects/osgi-test/src/test/java/org/mockito/osgitest/OsgiTest.java
+++ b/subprojects/osgi-test/src/test/java/org/mockito/osgitest/OsgiTest.java
@@ -13,6 +13,8 @@
import org.osgi.framework.Constants;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;
+import org.mockito.internal.configuration.plugins.Plugins;
+import org.mockito.plugins.InlineMockMaker;
import java.io.File;
import java.io.IOException;
@@ -51,6 +53,10 @@ private static Class>[] setUpClasses() throws Exception {
Map configuration = new HashMap<>();
configuration.put(Constants.FRAMEWORK_STORAGE, frameworkStorage.toString());
configuration.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, String.join(",", EXTRA_SYSTEMPACKAGES));
+ // When inline mock macker is used, the new 'mockitoboot.jar' is created on the fly
+ // and added to boot classloader (by instrumentation). As such, the following
+ // packages have to be explicitly delegated to boot classloader.
+ configuration.put(Constants.FRAMEWORK_BOOTDELEGATION, "org.mockito.internal.creation.bytebuddy.inject");
framework = frameworkFactory.newFramework(configuration);
framework.init();
BundleContext bundleContext = framework.getBundleContext();
@@ -79,11 +85,21 @@ private static Class>[] setUpClasses() throws Exception {
}
private static Class>[] getTestClasses() throws Exception {
- return new Class>[] {
- loadTestClass("SimpleMockTest"),
- loadTestClass("MockNonPublicClassFailsTest"),
- loadTestClass("MockClassInOtherBundleTest")
- };
+ // The tests could not use 'Plugins' since 'org.mockito.internal' package is not exported.
+ // Making the decision which tests to run depending on mock maker instance.
+ if (Plugins.getMockMaker() instanceof InlineMockMaker) {
+ return new Class>[] {
+ loadTestClass("SimpleMockTest"),
+ loadTestClass("MockNonPublicClassTest"),
+ loadTestClass("MockClassInOtherBundleTest")
+ };
+ } else {
+ return new Class>[] {
+ loadTestClass("SimpleMockTest"),
+ loadTestClass("MockNonPublicClassFailsTest"),
+ loadTestClass("MockClassInOtherBundleTest")
+ };
+ }
}
@AfterClass
diff --git a/subprojects/osgi-test/src/testBundle/java/org/mockito/osgitest/testbundle/MockNonPublicClassTest.java b/subprojects/osgi-test/src/testBundle/java/org/mockito/osgitest/testbundle/MockNonPublicClassTest.java
new file mode 100644
index 0000000000..fedd45dab1
--- /dev/null
+++ b/subprojects/osgi-test/src/testBundle/java/org/mockito/osgitest/testbundle/MockNonPublicClassTest.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.osgitest.testbundle;
+
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+
+public class MockNonPublicClassTest {
+
+ static class NonPublicClass {}
+
+ @Test
+ public void test_non_public_class() {
+ NonPublicClass nonPublicClass = mock(NonPublicClass.class);
+ }
+}
diff --git a/subprojects/programmatic-test/programmatic-test.gradle b/subprojects/programmatic-test/programmatic-test.gradle
new file mode 100644
index 0000000000..39db6c6442
--- /dev/null
+++ b/subprojects/programmatic-test/programmatic-test.gradle
@@ -0,0 +1,22 @@
+plugins {
+ id 'java'
+}
+
+description = "Test suite for excercising programmatic mock maker in Mockito"
+
+apply from: "$rootDir/gradle/dependencies.gradle"
+
+dependencies {
+ implementation project.rootProject
+ testImplementation libraries.junit4
+ testImplementation libraries.assertj
+}
+
+tasks.javadoc.enabled = false
+
+sourceCompatibility = 11
+targetCompatibility = 11
+
+test {
+ forkEvery = 1
+}
diff --git a/subprojects/programmatic-test/src/test/java/org/mockito/ProgrammaticMockMakerTest.java b/subprojects/programmatic-test/src/test/java/org/mockito/ProgrammaticMockMakerTest.java
new file mode 100644
index 0000000000..f03555d0e5
--- /dev/null
+++ b/subprojects/programmatic-test/src/test/java/org/mockito/ProgrammaticMockMakerTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2022 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.withSettings;
+
+import org.junit.Test;
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.exceptions.verification.SmartNullPointerException;
+import org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker;
+import org.mockito.invocation.MockHandler;
+import org.mockito.mock.MockCreationSettings;
+
+public final class ProgrammaticMockMakerTest {
+ @Test
+ public void test_normal_mock_uses_given_mock_maker() {
+ ClassWithFinalMethod inlineMock =
+ Mockito.mock(
+ ClassWithFinalMethod.class, withSettings().mockMaker(MockMakers.INLINE));
+ ClassWithFinalMethod subclassMock =
+ Mockito.mock(
+ ClassWithFinalMethod.class, withSettings().mockMaker(MockMakers.SUBCLASS));
+
+ Mockito.when(inlineMock.finalMethodCallingNonFinal()).thenReturn("MOCKED");
+ Mockito.when(subclassMock.finalMethodCallingNonFinal()).thenReturn("MOCKED");
+
+ assertEquals("MOCKED", inlineMock.finalMethodCallingNonFinal());
+ assertEquals("ORIGINAL", subclassMock.finalMethodCallingNonFinal());
+ assertEquals("MOCKED", subclassMock.nonFinal());
+ }
+
+ @Test
+ public void test_mockability_check_uses_given_mock_maker() {
+ assertNotNull(Mockito.mock(FinalClass.class, withSettings().mockMaker(MockMakers.INLINE)));
+ assertThrows(
+ MockitoException.class,
+ () ->
+ Mockito.mock(
+ FinalClass.class, withSettings().mockMaker(MockMakers.SUBCLASS)));
+ }
+
+ @Test
+ public void test_deep_stups_inherit_mock_maker() {
+ Container inlineMock =
+ Mockito.mock(
+ Container.class,
+ withSettings()
+ .mockMaker(MockMakers.INLINE)
+ .defaultAnswer(Answers.RETURNS_DEEP_STUBS));
+ Container subclassMock =
+ Mockito.mock(
+ Container.class,
+ withSettings()
+ .mockMaker(MockMakers.SUBCLASS)
+ .defaultAnswer(Answers.RETURNS_DEEP_STUBS));
+
+ assertNotNull(inlineMock.finalClass());
+ assertNotNull(inlineMock.subContainer().finalClass());
+ assertNull(inlineMock.finalClass().someMethod());
+ assertNull(inlineMock.subContainer().finalClass().someMethod());
+ assertNull(inlineMock.classWithFinalMethod().finalMethod());
+ assertNull(inlineMock.subContainer().classWithFinalMethod().finalMethod());
+
+ assertNull(subclassMock.finalClass());
+ assertNull(subclassMock.subContainer().finalClass());
+ assertEquals("ORIGINAL", subclassMock.classWithFinalMethod().finalMethod());
+ assertEquals("ORIGINAL", subclassMock.subContainer().classWithFinalMethod().finalMethod());
+ }
+
+ @Test
+ public void test_returned_mocks_inherit_mock_maker() {
+ Container inlineMock =
+ Mockito.mock(
+ Container.class,
+ withSettings()
+ .mockMaker(MockMakers.INLINE)
+ .defaultAnswer(Answers.RETURNS_MOCKS));
+ Container subclassMock =
+ Mockito.mock(
+ Container.class,
+ withSettings()
+ .mockMaker(MockMakers.SUBCLASS)
+ .defaultAnswer(Answers.RETURNS_MOCKS));
+
+ assertNotNull(inlineMock.finalClass());
+ assertNotNull(inlineMock.subContainer().finalClass());
+ assertEquals("", inlineMock.finalClass().someMethod());
+ assertEquals("", inlineMock.subContainer().finalClass().someMethod());
+ assertEquals("", inlineMock.classWithFinalMethod().finalMethod());
+ assertEquals("", inlineMock.subContainer().classWithFinalMethod().finalMethod());
+
+ assertNull(subclassMock.finalClass());
+ assertNull(subclassMock.subContainer().finalClass());
+ assertEquals("ORIGINAL", subclassMock.classWithFinalMethod().finalMethod());
+ assertEquals("ORIGINAL", subclassMock.subContainer().classWithFinalMethod().finalMethod());
+ }
+
+ @Test
+ public void test_smart_nulls_inherit_mock_maker() {
+ Container inlineMock =
+ Mockito.mock(
+ Container.class,
+ withSettings()
+ .mockMaker(MockMakers.INLINE)
+ .defaultAnswer(Answers.RETURNS_SMART_NULLS));
+ Container subclassMock =
+ Mockito.mock(
+ Container.class,
+ withSettings()
+ .mockMaker(MockMakers.SUBCLASS)
+ .defaultAnswer(Answers.RETURNS_SMART_NULLS));
+
+ assertNotNull(inlineMock.finalClass());
+ assertNotNull(inlineMock.classWithFinalMethod());
+ assertThrows(SmartNullPointerException.class, () -> inlineMock.finalClass().someMethod());
+ assertThrows(
+ SmartNullPointerException.class,
+ () -> inlineMock.classWithFinalMethod().finalMethod());
+
+ assertNull(subclassMock.finalClass());
+ assertNotNull(subclassMock.classWithFinalMethod());
+ assertEquals("ORIGINAL", subclassMock.classWithFinalMethod().finalMethod());
+ }
+
+ @Test
+ public void test_custom_mock_maker() {
+ assertThatThrownBy(
+ () -> {
+ Mockito.mock(
+ Container.class,
+ withSettings().mockMaker(CustomMockMaker.class.getName()));
+ })
+ .hasMessage("CUSTOM MOCK MAKER");
+ }
+
+ @Test
+ public void test_exception_when_mock_maker_cannot_be_instantiated() {
+ class InvalidMockMaker extends SubclassByteBuddyMockMaker {
+ // Local classes have an implicit constructor parameter,
+ // which makes them an invalid mock maker.
+ }
+ assertThatThrownBy(
+ () -> {
+ Mockito.mock(
+ Container.class,
+ withSettings().mockMaker(InvalidMockMaker.class.getName()));
+ })
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Failed to construct MockMaker")
+ .hasMessageContaining(InvalidMockMaker.class.getName());
+ }
+
+ private static final class FinalClass {
+ String someMethod() {
+ return "ORIGINAL";
+ }
+ }
+
+ private static class ClassWithFinalMethod {
+ final String finalMethod() {
+ return "ORIGINAL";
+ }
+
+ final String finalMethodCallingNonFinal() {
+ nonFinal();
+ return "ORIGINAL";
+ }
+
+ String nonFinal() {
+ return "ORIGINAL";
+ }
+ }
+
+ private static class Container {
+ FinalClass finalClass() {
+ return new FinalClass();
+ }
+
+ ClassWithFinalMethod classWithFinalMethod() {
+ return new ClassWithFinalMethod();
+ }
+
+ Container subContainer() {
+ return new Container();
+ }
+ }
+
+ public static class CustomMockMaker extends SubclassByteBuddyMockMaker {
+ @Override
+ public T createMock(MockCreationSettings settings, MockHandler handler) {
+ throw new RuntimeException("CUSTOM MOCK MAKER");
+ }
+ }
+}
diff --git a/subprojects/programmatic-test/src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java b/subprojects/programmatic-test/src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java
new file mode 100644
index 0000000000..2d495bed00
--- /dev/null
+++ b/subprojects/programmatic-test/src/test/java/org/mockitousage/annotation/ProgrammaticMockMakerAnnotationTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2022 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockitousage.annotation;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockMakers;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+public class ProgrammaticMockMakerAnnotationTest {
+ @Mock(mockMaker = MockMakers.INLINE)
+ ClassWithFinalMethod inlineMock;
+
+ @Mock(mockMaker = MockMakers.SUBCLASS)
+ ClassWithFinalMethod subclassMock;
+
+ @Before
+ public void init() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ public void test_mock_uses_given_mock_maker() {
+ Mockito.when(inlineMock.finalMethodCallingNonFinal()).thenReturn("MOCKED");
+ Mockito.when(subclassMock.finalMethodCallingNonFinal()).thenReturn("MOCKED");
+
+ assertEquals("MOCKED", inlineMock.finalMethodCallingNonFinal());
+ assertEquals("ORIGINAL", subclassMock.finalMethodCallingNonFinal());
+ assertEquals("MOCKED", subclassMock.nonFinal());
+ }
+
+ private static class ClassWithFinalMethod {
+ final String finalMethodCallingNonFinal() {
+ nonFinal();
+ return "ORIGINAL";
+ }
+
+ String nonFinal() {
+ return "ORIGINAL";
+ }
+ }
+}
diff --git a/subprojects/proxy/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor b/subprojects/proxy/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor
new file mode 100644
index 0000000000..71111e3378
--- /dev/null
+++ b/subprojects/proxy/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor
@@ -0,0 +1 @@
+member-accessor-reflection
diff --git a/subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor b/subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor
similarity index 100%
rename from subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor
rename to subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor
diff --git a/subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker b/subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000000..fdbd0b1579
--- /dev/null
+++ b/subprojects/subclass/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-subclass
diff --git a/subprojects/subclass/src/test/java/org/mockitosubclass/PluginTest.java b/subprojects/subclass/src/test/java/org/mockitosubclass/PluginTest.java
new file mode 100644
index 0000000000..4acd77899f
--- /dev/null
+++ b/subprojects/subclass/src/test/java/org/mockitosubclass/PluginTest.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2022 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockitosubclass;
+
+import org.junit.Test;
+import org.mockito.internal.configuration.plugins.Plugins;
+import org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker;
+import org.mockito.internal.util.reflection.ModuleMemberAccessor;
+
+import static org.junit.Assert.*;
+
+public class PluginTest {
+
+ @Test
+ public void mock_maker_should_be_inline() throws Exception {
+ assertTrue(Plugins.getMockMaker() instanceof ByteBuddyMockMaker);
+ }
+
+ @Test
+ public void member_accessor_should_be_module() throws Exception {
+ assertTrue(Plugins.getMemberAccessor() instanceof ModuleMemberAccessor);
+ }
+
+}
diff --git a/subprojects/subclass/subclass.gradle b/subprojects/subclass/subclass.gradle
new file mode 100644
index 0000000000..8ef7ae1ae5
--- /dev/null
+++ b/subprojects/subclass/subclass.gradle
@@ -0,0 +1,11 @@
+description = "Mockito preconfigured subclass mock maker"
+
+apply from: "$rootDir/gradle/java-library.gradle"
+
+dependencies {
+ api project.rootProject
+ testImplementation libraries.junit4
+ testImplementation libraries.assertj
+}
+
+tasks.javadoc.enabled = false