strict constructor injection strategy to not allow semi-initialized fields at this step
+ *
lenient field/property injection strategy to skip fields without no-args constructor
+ *
+ *
+ * @param needingInjection fields needing injection
+ * @param mocks mocks available for injection
+ * @param testClassInstance instance of the test
+ */
+ public void injectOngoingMocksOnFields(Set needingInjection, Set
*
*
- * If the field is also annotated with the compatible @InjectMocks then the field will be ignored,
- * The injection engine will handle this specific case.
+ * If the field is also annotated with the compatible @InjectMocks and has
+ * parameterized constructor then the field will be ignored, the injection engine will handle this
+ * specific case.
*
*
*
This engine will fail, if the field is also annotated with incompatible Mockito annotations.
@@ -50,7 +51,7 @@ public class SpyAnnotationEngine implements AnnotationEngine, org.mockito.config
public void process(Class> context, Object testInstance) {
Field[] fields = context.getDeclaredFields();
for (Field field : fields) {
- if (field.isAnnotationPresent(Spy.class) && !field.isAnnotationPresent(InjectMocks.class)) {
+ if (shouldProcess(field)) {
assertNoIncompatibleAnnotations(Spy.class, field, Mock.class, Captor.class);
field.setAccessible(true);
Object instance;
@@ -72,6 +73,19 @@ public void process(Class> context, Object testInstance) {
}
}
+ private boolean shouldProcess(Field field) {
+ if (!field.isAnnotationPresent(Spy.class)) {
+ return false;
+ }
+ if (!field.isAnnotationPresent(InjectMocks.class)) {
+ return true;
+ }
+ if (field.getType().isInterface()) {
+ return false;
+ }
+ return !hasParameterizedConstructor(field.getType());
+ }
+
private static Object spyInstance(Field field, Object instance) {
return Mockito.mock(instance.getClass(),
withSettings().spiedInstance(instance)
@@ -116,6 +130,15 @@ private static Object spyNewInstance(Object testInstance, Field field)
}
}
+ private static boolean hasParameterizedConstructor(Class> type) {
+ for (Constructor> constructor : type.getDeclaredConstructors()) {
+ if (constructor.getParameterTypes().length > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static Constructor> noArgConstructorOf(Class> type) {
Constructor> constructor;
try {
diff --git a/src/main/java/org/mockito/internal/configuration/injection/ConstructorInjection.java b/src/main/java/org/mockito/internal/configuration/injection/ConstructorInjection.java
index 380fb93da1..2e8ab0665b 100644
--- a/src/main/java/org/mockito/internal/configuration/injection/ConstructorInjection.java
+++ b/src/main/java/org/mockito/internal/configuration/injection/ConstructorInjection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007 Mockito contributors
+ * Copyright (c) 2020 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal.configuration.injection;
@@ -8,45 +8,32 @@
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Set;
import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.util.reflection.ConstructorResolver;
+import org.mockito.internal.util.reflection.ConstructorResolver.BiggestConstructorResolver;
import org.mockito.internal.util.reflection.FieldInitializationReport;
import org.mockito.internal.util.reflection.FieldInitializer;
-import org.mockito.internal.util.reflection.FieldInitializer.ConstructorArgumentResolver;
/**
* Injection strategy based on constructor.
*
*
* The strategy will search for the constructor with most parameters
- * and try to resolve mocks by type.
- *
- *
- *
- * TODO on missing mock type, shall it abandon or create "noname" mocks.
- * TODO and what if the arg type is not mockable.
- *
- *
- *
- * For now the algorithm tries to create anonymous mocks if an argument type is missing.
- * If not possible the algorithm abandon resolution.
+ * and try to resolve mocks by type, or null if there is no mocks matching a parameter.
*
*/
public class ConstructorInjection extends MockInjectionStrategy {
- public ConstructorInjection() { }
-
public boolean processInjection(Field field, Object fieldOwner, Set mockCandidates) {
try {
- SimpleArgumentResolver simpleArgumentResolver = new SimpleArgumentResolver(mockCandidates);
- FieldInitializationReport report = new FieldInitializer(fieldOwner, field, simpleArgumentResolver).initialize();
+ ConstructorResolver constructorResolver = createConstructorResolver(field.getType(), mockCandidates);
+ FieldInitializationReport report = new FieldInitializer(fieldOwner, field, constructorResolver).initialize();
- return report.fieldWasInitializedUsingContructorArgs();
+ return report.fieldWasInitialized();
} catch (MockitoException e) {
- if(e.getCause() instanceof InvocationTargetException) {
+ if (e.getCause() instanceof InvocationTargetException) {
Throwable realCause = e.getCause().getCause();
throw fieldInitialisationThrewException(field, realCause);
}
@@ -56,30 +43,8 @@ public boolean processInjection(Field field, Object fieldOwner, Set mock
}
- /**
- * Returns mocks that match the argument type, if not possible assigns null.
- */
- static class SimpleArgumentResolver implements ConstructorArgumentResolver {
- final Set objects;
-
- public SimpleArgumentResolver(Set objects) {
- this.objects = objects;
- }
-
- public Object[] resolveTypeInstances(Class>... argTypes) {
- List argumentInstances = new ArrayList(argTypes.length);
- for (Class> argType : argTypes) {
- argumentInstances.add(objectThatIsAssignableFrom(argType));
- }
- return argumentInstances.toArray();
- }
-
- private Object objectThatIsAssignableFrom(Class> argType) {
- for (Object object : objects) {
- if(argType.isAssignableFrom(object.getClass())) return object;
- }
- return null;
- }
+ protected ConstructorResolver createConstructorResolver(Class> fieldType, Set mockCandidates) {
+ return new BiggestConstructorResolver(fieldType, mockCandidates);
}
}
diff --git a/src/main/java/org/mockito/internal/configuration/injection/LenientPropertyAndSetterInjection.java b/src/main/java/org/mockito/internal/configuration/injection/LenientPropertyAndSetterInjection.java
new file mode 100644
index 0000000000..84a44b7605
--- /dev/null
+++ b/src/main/java/org/mockito/internal/configuration/injection/LenientPropertyAndSetterInjection.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.configuration.injection;
+
+import org.mockito.internal.util.reflection.ConstructorResolver;
+import org.mockito.internal.util.reflection.ConstructorResolver.LenientNoArgsConstructorResolver;
+
+/**
+ * Inject mocks using setters then fields, if no setters available, see
+ * {@link PropertyAndSetterInjection parent class} for more information on algorithm.
+ *
+ * The strategy to instantiate field (if needed) is to try to find no-args constructor on field type
+ * and skip the field otherwise.
+ *
- * Note: If the field needing injection is not initialized, the strategy tries
- * to create one using a no-arg constructor of the field type.
+ * Note: If the field needing injection is not initialized, the strategy tries to create one
+ * using a no-arg constructor of the field type or fails with an explicit message.
*
*/
public class PropertyAndSetterInjection extends MockInjectionStrategy {
@@ -77,6 +79,10 @@ public boolean isOut(Field object) {
public boolean processInjection(Field injectMocksField, Object injectMocksFieldOwner, Set mockCandidates) {
FieldInitializationReport report = initializeInjectMocksField(injectMocksField, injectMocksFieldOwner);
+ if (!report.fieldIsInitialized()) {
+ return false;
+ }
+
// for each field in the class hierarchy
boolean injectionOccurred = false;
Class> fieldClass = report.fieldClass();
@@ -90,7 +96,8 @@ public boolean processInjection(Field injectMocksField, Object injectMocksFieldO
private FieldInitializationReport initializeInjectMocksField(Field field, Object fieldOwner) {
try {
- return new FieldInitializer(fieldOwner, field).initialize();
+ final ConstructorResolver constructorResolver = createConstructorResolver(field.getType());
+ return new FieldInitializer(fieldOwner, field, constructorResolver).initialize();
} catch (MockitoException e) {
if(e.getCause() instanceof InvocationTargetException) {
Throwable realCause = e.getCause().getCause();
@@ -100,6 +107,9 @@ private FieldInitializationReport initializeInjectMocksField(Field field, Object
}
}
+ protected ConstructorResolver createConstructorResolver(Class> fieldType) {
+ return new NoArgsConstructorResolver(fieldType);
+ }
private boolean injectMockCandidates(Class> awaitingInjectionClazz, Object injectee, Set mocks) {
boolean injectionOccurred;
diff --git a/src/main/java/org/mockito/internal/configuration/injection/StrictConstructorInjection.java b/src/main/java/org/mockito/internal/configuration/injection/StrictConstructorInjection.java
new file mode 100644
index 0000000000..c2dc4a61b3
--- /dev/null
+++ b/src/main/java/org/mockito/internal/configuration/injection/StrictConstructorInjection.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2020 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.configuration.injection;
+
+import java.util.Set;
+
+import org.mockito.internal.util.reflection.ConstructorResolver;
+import org.mockito.internal.util.reflection.ConstructorResolver.StrictBiggestConstructorResolver;
+
+/**
+ * Injection strategy based on constructor.
+ *
+ * The strategy will search for the constructor with most parameters and try to resolve mocks by
+ * type or skip the field if there is no mocks matching a parameter.
+ *
+ */
+public class StrictConstructorInjection extends ConstructorInjection {
+
+ @Override
+ protected ConstructorResolver createConstructorResolver(Class> fieldType, Set mockCandidates) {
+ return new StrictBiggestConstructorResolver(fieldType, mockCandidates);
+ }
+
+}
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 4644e218aa..2c0fdb393f 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
@@ -36,7 +36,8 @@ public Object thenInject() {
setField(injectee, candidateFieldToBeInjected,matchingMock);
}
} catch (RuntimeException e) {
- throw cannotInjectDependency(candidateFieldToBeInjected, matchingMock, e);
+ final Throwable details = e.getCause() == null ? e : e.getCause();
+ throw cannotInjectDependency(candidateFieldToBeInjected, matchingMock, details);
}
return matchingMock;
}
diff --git a/src/main/java/org/mockito/internal/configuration/injection/scanner/MockScanner.java b/src/main/java/org/mockito/internal/configuration/injection/scanner/MockScanner.java
index b7da87246b..982e447424 100644
--- a/src/main/java/org/mockito/internal/configuration/injection/scanner/MockScanner.java
+++ b/src/main/java/org/mockito/internal/configuration/injection/scanner/MockScanner.java
@@ -42,7 +42,22 @@ public MockScanner(Object instance, Class> clazz) {
* @param mocks Set of mocks
*/
public void addPreparedMocks(Set mocks) {
- mocks.addAll(scan());
+ scan(clazz, mocks);
+ }
+
+ /**
+ * Scan and prepare mocks for the whole hierarchy of given testClassInstance.
+ *
+ * @return A prepared set of mock
+ */
+ public Set scanHierarchy() {
+ final Set mocks = newMockSafeHashSet();
+ Class> currentClass = clazz;
+ while (currentClass != Object.class) {
+ scan(currentClass, mocks);
+ currentClass = currentClass.getSuperclass();
+ }
+ return mocks;
}
/**
@@ -50,8 +65,7 @@ public void addPreparedMocks(Set mocks) {
*
* @return A prepared set of mock
*/
- private Set scan() {
- Set mocks = newMockSafeHashSet();
+ private void scan(Class> clazz, Set mocks) {
for (Field field : clazz.getDeclaredFields()) {
// mock or spies only
FieldReader fieldReader = new FieldReader(instance, field);
@@ -61,7 +75,6 @@ private Set scan() {
mocks.add(mockInstance);
}
}
- return mocks;
}
private Object preparedMock(Object instance, Field field) {
diff --git a/src/main/java/org/mockito/internal/exceptions/Reporter.java b/src/main/java/org/mockito/internal/exceptions/Reporter.java
index f661b81e5c..3f69e0f44d 100644
--- a/src/main/java/org/mockito/internal/exceptions/Reporter.java
+++ b/src/main/java/org/mockito/internal/exceptions/Reporter.java
@@ -16,7 +16,21 @@
import org.mockito.exceptions.base.MockitoAssertionError;
import org.mockito.exceptions.base.MockitoException;
-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;
@@ -726,7 +740,7 @@ public static MockitoException invocationListenerThrewException(InvocationListen
"threw an exception : " + listenerThrowable.getClass().getName() + listenerThrowable.getMessage()), listenerThrowable);
}
- public static MockitoException cannotInjectDependency(Field field, Object matchingMock, Exception details) {
+ public static MockitoException cannotInjectDependency(Field field, Object matchingMock, Throwable details) {
return new MockitoException(join(
"Mockito couldn't inject mock dependency '" + MockUtil.getMockName(matchingMock) + "' on field ",
"'" + field + "'",
@@ -736,7 +750,7 @@ public static MockitoException cannotInjectDependency(Field field, Object matchi
), details);
}
- private static String exceptionCauseMessageIfAvailable(Exception details) {
+ private static String exceptionCauseMessageIfAvailable(Throwable details) {
if (details.getCause() == null) {
return details.getMessage();
}
diff --git a/src/main/java/org/mockito/internal/util/reflection/ConstructorResolver.java b/src/main/java/org/mockito/internal/util/reflection/ConstructorResolver.java
new file mode 100644
index 0000000000..2e1fc1002a
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/ConstructorResolver.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.util.MockUtil;
+
+/**
+ * Represents the strategy used to resolve a constructor and its arguments.
+ */
+public interface ConstructorResolver {
+
+ /**
+ * Indicates whether this resolver is able to resolve a constructor and its arguments, based on
+ * type to resolve and resolver strategy.
+ *
+ * @return {@code true} if a constructor and its arguments are resolvable, {@code false}
+ * otherwise.
+ * @throws MockitoException implementation may choose to throw if contructor and/or its
+ * arguments are not resolvable for some reason.
+ */
+ boolean isResolvable() throws MockitoException;
+
+ /**
+ * Resolves constructor.
+ *
+ * If {@link #isResolvable()} is {@code true} this method must return a constructor.
+ *
+ *
+ * @return constructor, should not be null.
+ * @throws MockitoException implementation can throw if contructor is not resolvable for some
+ * reason (when {only @link #isResolvable()} is not {@code true}).
+ */
+ Constructor> resolveConstructor() throws MockitoException;
+
+ /**
+ * Resolves constructor arguments instances matching parameters of constructor resolved by
+ * {@link #resolveConstructor()}.
+ *
+ * If {@link #isResolvable()} is {@code true} this method must return correct arguments.
+ *
+ *
+ * @return array of argument instances to be given to the constructor, should not be null.
+ * @throws MockitoException implementation can throw if arguments are not resolvable for some
+ * reason (only when {@link #isResolvable()} is not {@code true}).
+ */
+ Object[] resolveArguments() throws MockitoException;
+
+ /**
+ * Resolves the no-arguments constructor (or default constructor) of the given type.
+ *
+ * This resolver throws exception if given type has only parameterized constructors.
+ *
+ */
+ class NoArgsConstructorResolver implements ConstructorResolver {
+ protected final Class> type;
+ private Constructor> constructor;
+
+ /**
+ * Prepares Resolver to resolve constructor of given type.
+ *
+ * @param type class to resolve constructor
+ */
+ public NoArgsConstructorResolver(Class> type) {
+ this.type = type;
+ }
+
+ /**
+ * @return {@code true} if there is a no-arguments constructor, throws otherwise.
+ * @throws MockitoException throws if given type has only parameterized constructors.
+ */
+ @Override
+ public boolean isResolvable() throws MockitoException {
+ return resolveConstructor() != null;
+ }
+
+ /**
+ * @return the no-arguments constructor.
+ * @throws MockitoException throws if given type has only parameterized constructors.
+ */
+ @Override
+ public Constructor> resolveConstructor() throws MockitoException {
+ if (constructor == null) {
+ try {
+ constructor = type.getDeclaredConstructor();
+ } catch (NoSuchMethodException e) {
+ throw new MockitoException("the type '" + type.getSimpleName() + "' has no default constructor", e);
+ }
+ }
+ return constructor;
+ }
+
+ /**
+ * @return an empty array matching no-arguments constructor.
+ */
+ @Override
+ public Object[] resolveArguments() {
+ return new Object[0];
+ }
+ }
+
+ /**
+ * Resolves the no-arguments constructor (or default constructor) of the given type.
+ *
+ */
+ class LenientNoArgsConstructorResolver extends NoArgsConstructorResolver {
+
+ /**
+ * Prepares Resolver to resolve constructor of given type.
+ *
+ * @param type class to resolve constructor
+ */
+ public LenientNoArgsConstructorResolver(Class> type) {
+ super(type);
+ }
+
+ /**
+ * @return {@code true} if there is a no-arguments constructor, {@code false} otherwise.
+ */
+ @Override
+ public boolean isResolvable() {
+ try {
+ return resolveConstructor() != null;
+ } catch (MockitoException e) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Resolves the constructor with the highest number of parameters and, in case of egality, the
+ * highest number of mockable parameters.
+ * Try to resolve actual constuctor arguments instances with given mocks matching constructor
+ * parameters, or null if there is no mocks matching a parameter.
+ *
+ *
+ * TODO on missing mock type, shall it abandon or create "noname" mocks.
+ * TODO and what if the arg type is not mockable.
+ *
+ */
+ class BiggestConstructorResolver implements ConstructorResolver {
+ protected final Class> type;
+ protected final Set mocks;
+
+ protected final Comparator> byParameterNumber = new Comparator>() {
+ public int compare(Constructor> constructorA, Constructor> constructorB) {
+ int argLengths = constructorB.getParameterTypes().length - constructorA.getParameterTypes().length;
+ if (argLengths == 0) {
+ int constructorAMockableParamsSize = countMockableParams(constructorA);
+ int constructorBMockableParamsSize = countMockableParams(constructorB);
+ return constructorBMockableParamsSize - constructorAMockableParamsSize;
+ }
+ return argLengths;
+ }
+
+ private int countMockableParams(Constructor> constructor) {
+ int constructorMockableParamsSize = 0;
+ for (Class> aClass : constructor.getParameterTypes()) {
+ if (MockUtil.typeMockabilityOf(aClass).mockable()) {
+ constructorMockableParamsSize++;
+ }
+ }
+ return constructorMockableParamsSize;
+ }
+ };
+
+ private Constructor> constructor;
+ private Object[] arguments;
+
+ /**
+ * Prepares Resolver to resolve constructor of given type and resolve constuctor arguments
+ * with given mocks.
+ *
+ * @param type class to resolve constructor
+ * @param mocks set of mocks instances
+ */
+ public BiggestConstructorResolver(Class> type, Set mocks) {
+ this.type = type;
+ this.mocks = mocks;
+ }
+
+ /**
+ * @return {@code true} if there is a parameterized constructor, throws otherwise.
+ * @throws MockitoException throws if given type has only no-arguments constructor.
+ */
+ @Override
+ public boolean isResolvable() throws MockitoException {
+ return resolveConstructor() != null;
+ }
+
+ /**
+ * @return constructor with the highest number of parameters and, in case of egality, the
+ * highest number of mockable parameters.
+ * @throws MockitoException throws if given type has only no-arguments constructor.
+ */
+ @Override
+ public Constructor> resolveConstructor() throws MockitoException {
+ if (constructor == null) {
+ final List extends Constructor>> constructors = Arrays.asList(type.getDeclaredConstructors());
+ Collections.sort(constructors, byParameterNumber);
+ final Constructor> constructor = constructors.get(0);
+ checkParameterized(constructor);
+ this.constructor = constructor;
+ }
+ return constructor;
+ }
+
+ /**
+ * @return an array of actual argument instances of given mocks matching constructor
+ * parameters, or null if there is no mocks matching a parameter.
+ */
+ @Override
+ public Object[] resolveArguments() {
+ if (arguments == null) {
+ final Class>[] argTypes = resolveConstructor().getParameterTypes();
+ final List argumentInstances = new ArrayList(argTypes.length);
+ for (Class> argType : argTypes) {
+ argumentInstances.add(objectThatIsAssignableFrom(argType));
+ }
+ arguments = argumentInstances.toArray();
+ }
+ return arguments;
+ }
+
+ protected Object objectThatIsAssignableFrom(Class> argType) {
+ for (Object object : mocks) {
+ if (object != null && argType.isAssignableFrom(object.getClass())) {
+ return object;
+ }
+ }
+ return null;
+ }
+
+ protected void checkParameterized(Constructor> constructor) {
+ if (constructor.getParameterTypes().length == 0) {
+ throw new MockitoException("the type '" + type.getSimpleName() + "' has no parameterized constructor");
+ }
+ }
+ }
+
+ /**
+ * Resolves the constructor with the highest number of parameters and, in case of egality, the
+ * highest number of mockable parameters.
+ * Resolves actual constructor arguments instances with given mocks matching constructor
+ * parameters, or indicates there is no resolvable constructors if there is no mocks matching a
+ * parameter.
+ */
+ class StrictBiggestConstructorResolver extends BiggestConstructorResolver {
+
+ private boolean hasNullArguments = false;
+
+ /**
+ * Prepares Resolver to resolve constructor of given type and resolve constuctor arguments
+ * with given mocks.
+ *
+ * @param type class to resolve constructor
+ * @param mocks set of mocks instances
+ */
+ public StrictBiggestConstructorResolver(Class> type, Set mocks) {
+ super(type, mocks);
+ }
+
+ /**
+ * @return {@code true} if given mocks matches all arguments of constructor with the highest
+ * number of parameters, {@code false} otherwise.
+ * @throws MockitoException throws if given type has only no-arguments constructor.
+ */
+ @Override
+ public boolean isResolvable() throws MockitoException {
+ resolveArguments();
+ return !hasNullArguments;
+ }
+
+ @Override
+ protected Object objectThatIsAssignableFrom(Class> argType) {
+ final Object object = super.objectThatIsAssignableFrom(argType);
+ if (object == null) {
+ hasNullArguments = true;
+ }
+ return object;
+ }
+ }
+
+}
diff --git a/src/main/java/org/mockito/internal/util/reflection/FieldInitializationReport.java b/src/main/java/org/mockito/internal/util/reflection/FieldInitializationReport.java
index 957832ee00..035e4fd8ff 100644
--- a/src/main/java/org/mockito/internal/util/reflection/FieldInitializationReport.java
+++ b/src/main/java/org/mockito/internal/util/reflection/FieldInitializationReport.java
@@ -10,12 +10,18 @@
public class FieldInitializationReport {
private final Object fieldInstance;
private final boolean wasInitialized;
- private final boolean wasInitializedUsingConstructorArgs;
- public FieldInitializationReport(Object fieldInstance, boolean wasInitialized, boolean wasInitializedUsingConstructorArgs) {
+ /**
+ * Report field is not initialized.
+ */
+ public FieldInitializationReport() {
+ this.fieldInstance = null;
+ this.wasInitialized = false;
+ }
+
+ public FieldInitializationReport(Object fieldInstance, boolean wasInitialized) {
this.fieldInstance = fieldInstance;
this.wasInitialized = wasInitialized;
- this.wasInitializedUsingConstructorArgs = wasInitializedUsingConstructorArgs;
}
/**
@@ -28,21 +34,21 @@ public Object fieldInstance() {
}
/**
- * Indicate whether the field was created during the process or not.
+ * Indicate whether the field is initialized or not.
*
- * @return true if created, false if the field did already hold an instance.
+ * @return true if field is initialized, false otherwise.
*/
- public boolean fieldWasInitialized() {
- return wasInitialized;
+ public boolean fieldIsInitialized() {
+ return fieldInstance != null;
}
/**
- * Indicate whether the field was created using constructor args.
+ * Indicate whether the field was created during the process or not.
*
- * @return true if field was created using constructor parameters.
+ * @return true if created, false if the field did already hold an instance.
*/
- public boolean fieldWasInitializedUsingContructorArgs() {
- return wasInitializedUsingConstructorArgs;
+ public boolean fieldWasInitialized() {
+ return wasInitialized;
}
/**
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 e247d18c13..b947d8c20d 100644
--- a/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java
+++ b/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java
@@ -12,16 +12,12 @@
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
import org.mockito.exceptions.base.MockitoException;
-import org.mockito.internal.util.MockUtil;
/**
- * Initialize a field with type instance if a default constructor can be found.
+ * Initialize a field with type instance if a constructor can be found by the strategy of given
+ * {@link ConstructorResolver}.
*
*
* If the given field is already initialized, then the actual instance is returned.
@@ -33,8 +29,7 @@ public class FieldInitializer {
private final Object fieldOwner;
private final Field field;
- private final ConstructorInstantiator instantiator;
-
+ private final ConstructorResolver constructorResolver;
/**
* Prepare initializer with the given field on the given instance.
@@ -45,38 +40,19 @@ public class FieldInitializer {
*
* @param fieldOwner Instance of the test.
* @param field Field to be initialize.
+ * @param constructorResolver Constructor resolver strategy
*/
- public FieldInitializer(Object fieldOwner, Field field) {
- this(fieldOwner, field, new NoArgConstructorInstantiator(fieldOwner, field));
- }
-
- /**
- * Prepare initializer with the given field on the given instance.
- *
- *
- * This constructor fail fast if the field type cannot be handled.
- *
- *
- * @param fieldOwner Instance of the test.
- * @param field Field to be initialize.
- * @param argResolver Constructor parameters resolver
- */
- public FieldInitializer(Object fieldOwner, Field field, ConstructorArgumentResolver argResolver) {
- this(fieldOwner, field, new ParameterizedConstructorInstantiator(fieldOwner, field, argResolver));
- }
-
- private FieldInitializer(Object fieldOwner, Field field, ConstructorInstantiator instantiator) {
+ public FieldInitializer(Object fieldOwner, Field field, ConstructorResolver constructorResolver) {
if(new FieldReader(fieldOwner, field).isNull()) {
checkNotLocal(field);
checkNotInner(field);
checkNotInterface(field);
checkNotEnum(field);
checkNotAbstract(field);
-
}
this.fieldOwner = fieldOwner;
this.field = field;
- this.instantiator = instantiator;
+ this.constructorResolver = constructorResolver;
}
/**
@@ -132,173 +108,43 @@ private void checkNotEnum(Field field) {
private FieldInitializationReport acquireFieldInstance() throws IllegalAccessException {
Object fieldInstance = field.get(fieldOwner);
if(fieldInstance != null) {
- return new FieldInitializationReport(fieldInstance, false, false);
- }
-
- return instantiator.instantiate();
- }
-
- /**
- * Represents the strategy used to resolve actual instances
- * to be given to a constructor given the argument types.
- */
- public interface ConstructorArgumentResolver {
-
- /**
- * Try to resolve instances from types.
- *
- *
- * Checks on the real argument type or on the correct argument number
- * will happen during the field initialization {@link FieldInitializer#initialize()}.
- * I.e the only responsibility of this method, is to provide instances if possible.
- *
- *
- * @param argTypes Constructor argument types, should not be null.
- * @return The argument instances to be given to the constructor, should not be null.
- */
- Object[] resolveTypeInstances(Class>... argTypes);
- }
-
- private interface ConstructorInstantiator {
- FieldInitializationReport instantiate();
- }
-
- /**
- * Constructor instantiating strategy for no-arg constructor.
- *
- *
- * If a no-arg constructor can be found then the instance is created using
- * this constructor.
- * Otherwise a technical MockitoException is thrown.
- *
- */
- static class NoArgConstructorInstantiator implements ConstructorInstantiator {
- private final Object testClass;
- private final Field field;
-
- /**
- * Internal, checks are done by FieldInitializer.
- * Fields are assumed to be accessible.
- */
- NoArgConstructorInstantiator(Object testClass, Field field) {
- this.testClass = testClass;
- this.field = field;
+ return new FieldInitializationReport(fieldInstance, false);
}
- public FieldInitializationReport instantiate() {
- final AccessibilityChanger changer = new AccessibilityChanger();
- Constructor> constructor = null;
- try {
- constructor = field.getType().getDeclaredConstructor();
- changer.enableAccess(constructor);
-
- final Object[] noArg = new Object[0];
- Object newFieldInstance = constructor.newInstance(noArg);
- setField(testClass, field,newFieldInstance);
-
- return new FieldInitializationReport(field.get(testClass), true, false);
- } catch (NoSuchMethodException e) {
- throw new MockitoException("the type '" + field.getType().getSimpleName() + "' has no default constructor", e);
- } catch (InvocationTargetException e) {
- throw new MockitoException("the default constructor of type '" + field.getType().getSimpleName() + "' has raised an exception (see the stack trace for cause): " + e.getTargetException().toString(), e);
- } catch (InstantiationException e) {
- throw new MockitoException("InstantiationException (see the stack trace for cause): " + e.toString(), e);
- } catch (IllegalAccessException e) {
- throw new MockitoException("IllegalAccessException (see the stack trace for cause): " + e.toString(), e);
- } finally {
- if(constructor != null) {
- changer.safelyDisableAccess(constructor);
- }
- }
- }
+ return instantiate();
}
- /**
- * Constructor instantiating strategy for parameterized constructors.
- *
- *
- * Choose the constructor with the highest number of parameters, then
- * call the ConstructorArgResolver to get actual argument instances.
- * If the argResolver fail, then a technical MockitoException is thrown is thrown.
- * Otherwise the instance is created with the resolved arguments.
- *
- */
- static class ParameterizedConstructorInstantiator implements ConstructorInstantiator {
- private final Object testClass;
- private final Field field;
- private final ConstructorArgumentResolver argResolver;
- private final Comparator> byParameterNumber = new Comparator>() {
- public int compare(Constructor> constructorA, Constructor> constructorB) {
- int argLengths = constructorB.getParameterTypes().length - constructorA.getParameterTypes().length;
- if (argLengths == 0) {
- int constructorAMockableParamsSize = countMockableParams(constructorA);
- int constructorBMockableParamsSize = countMockableParams(constructorB);
- return constructorBMockableParamsSize - constructorAMockableParamsSize;
- }
- return argLengths;
- }
-
- private int countMockableParams(Constructor> constructor) {
- int constructorMockableParamsSize = 0;
- for (Class> aClass : constructor.getParameterTypes()) {
- if(MockUtil.typeMockabilityOf(aClass).mockable()){
- constructorMockableParamsSize++;
- }
- }
- return constructorMockableParamsSize;
- }
- };
-
- /**
- * Internal, checks are done by FieldInitializer.
- * Fields are assumed to be accessible.
- */
- ParameterizedConstructorInstantiator(Object testClass, Field field, ConstructorArgumentResolver argumentResolver) {
- this.testClass = testClass;
- this.field = field;
- this.argResolver = argumentResolver;
- }
-
- public FieldInitializationReport instantiate() {
- final AccessibilityChanger changer = new AccessibilityChanger();
- Constructor> constructor = null;
- try {
- constructor = biggestConstructor(field.getType());
- changer.enableAccess(constructor);
-
- final Object[] args = argResolver.resolveTypeInstances(constructor.getParameterTypes());
- Object newFieldInstance = constructor.newInstance(args);
- setField(testClass, field,newFieldInstance);
-
- return new FieldInitializationReport(field.get(testClass), false, true);
- } catch (IllegalArgumentException e) {
- throw new MockitoException("internal error : argResolver provided incorrect types for constructor " + constructor + " of type " + field.getType().getSimpleName(), e);
- } catch (InvocationTargetException e) {
- throw new MockitoException("the constructor of type '" + field.getType().getSimpleName() + "' has raised an exception (see the stack trace for cause): " + e.getTargetException().toString(), e);
- } catch (InstantiationException e) {
- throw new MockitoException("InstantiationException (see the stack trace for cause): " + e.toString(), e);
- } catch (IllegalAccessException e) {
- throw new MockitoException("IllegalAccessException (see the stack trace for cause): " + e.toString(), e);
- } finally {
- if(constructor != null) {
- changer.safelyDisableAccess(constructor);
- }
+ private FieldInitializationReport instantiate() {
+ final AccessibilityChanger changer = new AccessibilityChanger();
+ Constructor> constructor = null;
+ try {
+ if (!constructorResolver.isResolvable()) {
+ return new FieldInitializationReport();
}
- }
- private void checkParameterized(Constructor> constructor, Field field) {
- if(constructor.getParameterTypes().length == 0) {
- throw new MockitoException("the field " + field.getName() + " of type " + field.getType() + " has no parameterized constructor");
+ constructor = constructorResolver.resolveConstructor();
+ changer.enableAccess(constructor);
+
+ final Object[] args = constructorResolver.resolveArguments();
+ Object newFieldInstance = constructor.newInstance(args);
+ setField(fieldOwner, field, newFieldInstance);
+
+ return new FieldInitializationReport(newFieldInstance, true);
+ } catch (MockitoException e) {
+ throw new MockitoException("field '" + field.getName() + "': " + e.getMessage(), e);
+ } catch (IllegalArgumentException e) {
+ throw new MockitoException("internal error : " + constructorResolver.getClass().getSimpleName() + " provided incorrect types for constructor " + constructor + " of type " + field.getType().getSimpleName(), e);
+ } catch (InvocationTargetException e) {
+ throw new MockitoException("the constructor of type '" + field.getType().getSimpleName() + "' has raised an exception (see the stack trace for cause): " + e.getTargetException().toString(), e);
+ } catch (InstantiationException e) {
+ throw new MockitoException("InstantiationException (see the stack trace for cause): " + e.toString(), e);
+ } catch (IllegalAccessException e) {
+ throw new MockitoException("IllegalAccessException (see the stack trace for cause): " + e.toString(), e);
+ } finally {
+ if(constructor != null) {
+ changer.safelyDisableAccess(constructor);
}
}
-
- private Constructor> biggestConstructor(Class> clazz) {
- final List extends Constructor>> constructors = Arrays.asList(clazz.getDeclaredConstructors());
- Collections.sort(constructors, byParameterNumber);
-
- Constructor> constructor = constructors.get(0);
- checkParameterized(constructor, field);
- return constructor;
- }
}
+
}
diff --git a/src/test/java/org/mockito/internal/configuration/injection/ConstructorInjectionTest.java b/src/test/java/org/mockito/internal/configuration/injection/ConstructorInjectionTest.java
index 40231607a8..35a85689b7 100644
--- a/src/test/java/org/mockito/internal/configuration/injection/ConstructorInjectionTest.java
+++ b/src/test/java/org/mockito/internal/configuration/injection/ConstructorInjectionTest.java
@@ -1,11 +1,13 @@
/*
- * Copyright (c) 2007 Mockito contributors
+ * Copyright (c) 2020 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal.configuration.injection;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.lang.reflect.Field;
import java.util.HashSet;
@@ -16,15 +18,18 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.exceptions.base.MockitoException;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ConstructorInjectionTest {
@Mock private Observer observer;
- private ArgConstructor whatever;
+ public ArgConstructor whatever;
+ public NoArgsConstructor noArgsConstructor;
+ public ThrowingConstructor throwingConstructor;
- private ConstructorInjection underTest;
+ protected ConstructorInjection underTest;
@Before
public void initialize_dependencies() {
@@ -37,6 +42,35 @@ public void should_do_the_trick_of_instantiating() throws Exception {
assertTrue(result);
assertNotNull(whatever);
+ assertThat(whatever.observer).isEqualTo(observer);
+ }
+
+ @Test
+ public void should_instantiate_with_null_arg() throws Exception {
+ boolean result = underTest.process(field("whatever"), this, new HashSet());
+
+ assertThat(result).isTrue();
+ assertThat(whatever).isNotNull();
+ assertThat(whatever.observer).isNull();
+ }
+
+ @Test
+ public void should_not_instantiate_no_args_constructor() throws Exception {
+ boolean result = underTest.process(field("noArgsConstructor"), this, newSetOf(observer));
+
+ assertThat(result).isFalse();
+ assertThat(noArgsConstructor).isNull();
+ }
+
+ @Test
+ public void should_fail_to_instantiate_throwing_constructor() throws Exception {
+ try {
+ underTest.process(field("throwingConstructor"), this, newSetOf(observer));
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("Cannot instantiate").contains("throwingConstructor").contains("ThrowingConstructor").contains("business logic failed");
+ }
+ assertThat(throwingConstructor).isNull();
}
private Set newSetOf(Object item) {
@@ -45,11 +79,25 @@ private Set newSetOf(Object item) {
return mocks;
}
- private Field field(String fieldName) throws NoSuchFieldException {
- return this.getClass().getDeclaredField(fieldName);
+ protected Field field(String fieldName) throws NoSuchFieldException {
+ return this.getClass().getField(fieldName);
}
private static class ArgConstructor {
- ArgConstructor(Observer observer) {}
+ private final Observer observer;
+ ArgConstructor(Observer observer) {
+ this.observer = observer;
+ }
+ }
+
+ private static class NoArgsConstructor {
+ NoArgsConstructor() {}
+ }
+
+ private static class ThrowingConstructor {
+ ThrowingConstructor(Observer observer) throws Exception {
+ throw new NullPointerException("business logic failed");
+ }
}
+
}
diff --git a/src/test/java/org/mockito/internal/configuration/injection/LenientPropertyAndSetterInjectionTest.java b/src/test/java/org/mockito/internal/configuration/injection/LenientPropertyAndSetterInjectionTest.java
new file mode 100644
index 0000000000..1c41d7cd60
--- /dev/null
+++ b/src/test/java/org/mockito/internal/configuration/injection/LenientPropertyAndSetterInjectionTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.configuration.injection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.HashSet;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class LenientPropertyAndSetterInjectionTest extends PropertyAndSetterInjectionTest {
+
+ @Before
+ @Override
+ public void setup() {
+ underTest = new LenientPropertyAndSetterInjection();
+ }
+
+ @Override
+ public void should_fail_to_instantiate_one_arg_constructor() throws Exception {
+ // This test is not relevant for LenientPropertyAndSetterInjectionTest
+ }
+
+ @Test
+ public void should_not_instantiate_one_arg_constructor() throws Exception {
+ boolean result = underTest.process(field("oneArgConstructor"), this, new HashSet());
+
+ assertThat(result).isFalse();
+ assertThat(defaultConstructor).isNull();
+ }
+
+ @Override
+ public void should_fail_to_instantiate_vararg_constructor() throws Exception {
+ // This test is not relevant for LenientPropertyAndSetterInjectionTest
+ }
+
+ @Test
+ public void should_not_instantiate_vararg_constructor() throws Exception {
+ boolean result = underTest.process(field("varargConstructor"), this, new HashSet());
+
+ assertThat(result).isFalse();
+ assertThat(defaultConstructor).isNull();
+ }
+
+}
diff --git a/src/test/java/org/mockito/internal/configuration/injection/PropertyAndSetterInjectionTest.java b/src/test/java/org/mockito/internal/configuration/injection/PropertyAndSetterInjectionTest.java
new file mode 100644
index 0000000000..76a4ba6ddd
--- /dev/null
+++ b/src/test/java/org/mockito/internal/configuration/injection/PropertyAndSetterInjectionTest.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.configuration.injection;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Observer;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PropertyAndSetterInjectionTest {
+
+ @Mock
+ private Observer whatever;
+ @Mock
+ private Observer whichever;
+
+ public DefaultConstructor defaultConstructor;
+ public NoArgsConstructor noArgsConstructor;
+ public OneArgConstructor oneArgConstructor;
+ public VarargConstructor varargConstructor;
+ public ThrowingConstructor throwingConstructor;
+ public ThrowingSetter throwingSetter;
+
+ protected PropertyAndSetterInjection underTest;
+
+ @Before
+ public void setup() {
+ underTest = new PropertyAndSetterInjection();
+ }
+
+ @Test
+ public void should_instantiate_default_constructor() throws Exception {
+ boolean result = underTest.process(field("defaultConstructor"), this, newSetOf(whatever, whichever));
+
+ assertThat(result).isTrue();
+ assertThat(defaultConstructor).isNotNull();
+
+ }
+
+ @Test
+ public void should_instantiate_no_args_constructor() throws Exception {
+ boolean result = underTest.process(field("noArgsConstructor"), this, newSetOf(whatever));
+
+ assertThat(result).isTrue();
+ assertThat(noArgsConstructor).isNotNull();
+ }
+
+ @Test
+ public void should_instantiate_but_not_initialize_fields() throws Exception {
+ boolean result = underTest.process(field("defaultConstructor"), this, new HashSet());
+
+ assertThat(result).isFalse();
+ assertThat(defaultConstructor).isNotNull();
+ assertThat(defaultConstructor.whatever).isNull();
+ assertThat(defaultConstructor.other).isNull();
+ }
+
+ @Test
+ public void should_fail_to_instantiate_one_arg_constructor() throws Exception {
+ try {
+ underTest.process(field("oneArgConstructor"), this, newSetOf(whatever));
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("Cannot instantiate").contains("oneArgConstructor").contains("OneArgConstructor").contains("has no default constructor");
+ }
+ assertThat(throwingConstructor).isNull();
+ }
+
+ @Test
+ public void should_fail_to_instantiate_vararg_constructor() throws Exception {
+ try {
+ underTest.process(field("varargConstructor"), this, newSetOf(whatever));
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("Cannot instantiate").contains("varargConstructor").contains("VarargConstructor").contains("has no default constructor");
+ }
+ assertThat(throwingConstructor).isNull();
+ }
+
+ @Test
+ public void should_fail_to_instantiate_throwing_constructor() throws Exception {
+ try {
+ underTest.process(field("throwingConstructor"), this, newSetOf(whatever));
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("Cannot instantiate").contains("throwingConstructor").contains("ThrowingConstructor").contains("business logic failed");
+ }
+ assertThat(throwingConstructor).isNull();
+ }
+
+ @Test
+ public void should_initialize_fields_by_setter() throws Exception {
+ boolean result = underTest.process(field("defaultConstructor"), this, newSetOf(whatever));
+
+ assertThat(result).isTrue();
+ assertThat(defaultConstructor).isNotNull();
+ assertThat(defaultConstructor.whatever).isEqualTo(whatever);
+ assertThat(defaultConstructor.other).isEqualTo(whatever);
+
+ }
+
+ @Test
+ public void should_initialize_fields_by_property() throws Exception {
+ boolean result = underTest.process(field("noArgsConstructor"), this, newSetOf(whatever, whichever));
+
+ assertThat(result).isTrue();
+ assertThat(noArgsConstructor).isNotNull();
+ assertThat(noArgsConstructor.whatever).isEqualTo(whatever);
+ assertThat(noArgsConstructor.whichever).isEqualTo(whichever);
+
+ }
+
+ @Test
+ public void should_fail_to_initialize_fields_by_throwing_setter() throws Exception {
+ try {
+ underTest.process(field("throwingSetter"), this, newSetOf(whatever));
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("couldn't inject mock").contains("whatever").contains("Observer").contains("business logic failed");
+ }
+ assertThat(throwingSetter).isNotNull();
+ assertThat(throwingSetter.whatever).isNull();
+ }
+
+ private Set newSetOf(Object... mocks) {
+ return new HashSet(Arrays.asList(mocks));
+ }
+
+ protected Field field(String fieldName) throws NoSuchFieldException {
+ return this.getClass().getField(fieldName);
+ }
+
+ private static class NoArgsConstructor {
+ private Observer whatever;
+ private Observer whichever;
+ NoArgsConstructor() {
+ }
+ }
+
+ public static class DefaultConstructor {
+ private Observer whatever;
+ private Observer other;
+ public void setWhatever(Observer whatever) {
+ this.whatever = whatever;
+ this.other = whatever;
+ }
+ }
+
+ static class OneArgConstructor {
+ public OneArgConstructor(String whatever) {
+ }
+ }
+
+ static class VarargConstructor {
+ VarargConstructor(String... whatever) {
+ }
+ }
+
+ static class ThrowingConstructor {
+ ThrowingConstructor() {
+ throw new NullPointerException("business logic failed");
+ }
+ }
+
+ static class ThrowingSetter {
+ private Observer whatever;
+ public void setWhatever(Observer whatever) {
+ throw new NullPointerException("business logic failed");
+ }
+ }
+
+}
diff --git a/src/test/java/org/mockito/internal/configuration/injection/SimpleArgumentResolverTest.java b/src/test/java/org/mockito/internal/configuration/injection/SimpleArgumentResolverTest.java
deleted file mode 100644
index 70f5b497f3..0000000000
--- a/src/test/java/org/mockito/internal/configuration/injection/SimpleArgumentResolverTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2007 Mockito contributors
- * This program is made available under the terms of the MIT License.
- */
-package org.mockito.internal.configuration.injection;
-
-import static org.junit.Assert.*;
-
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
-import java.util.*;
-
-import org.junit.Test;
-
-public class SimpleArgumentResolverTest {
-
- @Test
- public void should_return_object_matching_given_types() throws Exception {
- ConstructorInjection.SimpleArgumentResolver resolver =
- new ConstructorInjection.SimpleArgumentResolver(newSetOf(new HashSet(), new ByteArrayOutputStream(), new HashMap()));
-
- Object[] resolvedInstance = resolver.resolveTypeInstances(Set.class, Map.class, OutputStream.class);
-
- assertEquals(3, resolvedInstance.length);
- assertTrue(resolvedInstance[0] instanceof Set);
- assertTrue(resolvedInstance[1] instanceof Map);
- assertTrue(resolvedInstance[2] instanceof OutputStream);
- }
-
- @Test
- public void should_return_null_when_match_is_not_possible_on_given_types() throws Exception {
- ConstructorInjection.SimpleArgumentResolver resolver =
- new ConstructorInjection.SimpleArgumentResolver(newSetOf(new HashSet(), new ByteArrayOutputStream()));
-
- Object[] resolvedInstance = resolver.resolveTypeInstances(Set.class, Map.class, OutputStream.class);
-
- assertEquals(3, resolvedInstance.length);
- assertTrue(resolvedInstance[0] instanceof Set);
- assertNull(resolvedInstance[1]);
- assertTrue(resolvedInstance[2] instanceof OutputStream);
- }
-
- @Test
- public void should_return_null_when_types_are_primitives() throws Exception {
- ConstructorInjection.SimpleArgumentResolver resolver =
- new ConstructorInjection.SimpleArgumentResolver(newSetOf(new HashMap(), new TreeSet()));
-
- Object[] resolvedInstance = resolver.resolveTypeInstances(Set.class, Map.class, Boolean.class);
-
- assertEquals(3, resolvedInstance.length);
- assertTrue(resolvedInstance[0] instanceof Set);
- assertTrue(resolvedInstance[1] instanceof Map);
- assertNull(resolvedInstance[2]);
- }
-
- private Set newSetOf(Object... objects) {
- return new HashSet(Arrays.asList(objects));
- }
-
-
-}
diff --git a/src/test/java/org/mockito/internal/configuration/injection/StrictConstructorInjectionTest.java b/src/test/java/org/mockito/internal/configuration/injection/StrictConstructorInjectionTest.java
new file mode 100644
index 0000000000..a86ff0a37a
--- /dev/null
+++ b/src/test/java/org/mockito/internal/configuration/injection/StrictConstructorInjectionTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.configuration.injection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.HashSet;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class StrictConstructorInjectionTest extends ConstructorInjectionTest {
+
+ @Before
+ public void initialize_dependencies() {
+ underTest = new StrictConstructorInjection();
+ }
+
+ @Override
+ public void should_instantiate_with_null_arg() throws Exception {
+ // This test is not relevant for StrictConstructorInjection
+ }
+
+ @Test
+ public void should_not_instantiate_with_null_arg() throws Exception {
+ boolean result = underTest.process(field("whatever"), this, new HashSet());
+
+ assertThat(result).isFalse();
+ assertThat(whatever).isNull();
+ }
+
+}
diff --git a/src/test/java/org/mockito/internal/util/reflection/BiggestConstructorResolverTest.java b/src/test/java/org/mockito/internal/util/reflection/BiggestConstructorResolverTest.java
new file mode 100644
index 0000000000..0d5e9b9900
--- /dev/null
+++ b/src/test/java/org/mockito/internal/util/reflection/BiggestConstructorResolverTest.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2020 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Observer;
+import java.util.Set;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.util.reflection.ConstructorResolver.BiggestConstructorResolver;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@SuppressWarnings("unchecked")
+@RunWith(MockitoJUnitRunner.class)
+public class BiggestConstructorResolverTest {
+
+ @Test
+ public void type_should_be_instantiable_if_resolver_provide_matching_types() throws Exception {
+ Observer observer = mock(Observer.class);
+ Map map = mock(Map.class);
+
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(MultipleConstructor.class, observer, map);
+ final Constructor> constructor = resolver.resolveConstructor();
+ final Object[] arguments = resolver.resolveArguments();
+ final Object newInstance = constructor.newInstance(arguments);
+
+ assertThat(newInstance).isNotNull().isInstanceOf(MultipleConstructor.class);
+ final MultipleConstructor multipleConstructor = (MultipleConstructor) newInstance;
+ assertThat(multipleConstructor.observer).isNotNull().isEqualTo(observer);
+ assertThat(multipleConstructor.map).isNotNull().isEqualTo(map);
+ }
+
+ @Test
+ public void type_should_be_instantiable_with_null_if_an_argument_instance_type_do_not_match_wanted_type() throws Exception {
+ Observer observer = mock(Observer.class);
+ Set> wrongArg = mock(Set.class);
+
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(MultipleConstructor.class, observer, wrongArg);
+ final Constructor> constructor = resolver.resolveConstructor();
+ final Object[] arguments = resolver.resolveArguments();
+ final Object newInstance = constructor.newInstance(arguments);
+
+ assertThat(newInstance).isNotNull().isInstanceOf(MultipleConstructor.class);
+ final MultipleConstructor multipleConstructor = (MultipleConstructor) newInstance;
+ assertThat(multipleConstructor.observer).isNotNull().isEqualTo(observer);
+ assertThat(multipleConstructor.map).isNull();
+ }
+
+ @Test
+ public void type_should_be_instantiable_with_vararg_constructor() throws Exception {
+ Observer[] vararg = new Observer[] { };
+ String string = "";
+
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(VarargConstructor.class, string, vararg);
+ final Constructor> constructor = resolver.resolveConstructor();
+ final Object[] arguments = resolver.resolveArguments();
+ final Object newInstance = constructor.newInstance(arguments);
+
+ assertThat(newInstance).isNotNull().isInstanceOf(VarargConstructor.class);
+ final VarargConstructor varargConstructor = (VarargConstructor) newInstance;
+ assertThat(varargConstructor.whatever).isNotNull().isEqualTo(string);
+ assertThat(varargConstructor.observers).isNotNull().isEqualTo(vararg);
+ }
+
+ @Test
+ public void type_should_be_instantiable_with_wrapper_constructor() throws Exception {
+ final HashSet hashSet = mock(HashSet.class);
+ final HashMap hashMap = mock(HashMap.class);
+
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(WrapperConstructor.class, hashSet, hashMap);
+ final Constructor> constructor = resolver.resolveConstructor();
+ final Object[] arguments = resolver.resolveArguments();
+ final Object newInstance = constructor.newInstance(arguments);
+
+ assertThat(newInstance).isNotNull().isInstanceOf(WrapperConstructor.class);
+ final WrapperConstructor wrapperConstructor = (WrapperConstructor) newInstance;
+ assertThat(wrapperConstructor.set).isNotNull().isEqualTo(hashSet);
+ assertThat(wrapperConstructor.map).isNotNull().isEqualTo(hashMap);
+ assertThat(wrapperConstructor.bool).isNull();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void should_fail_to_instantiate_type_with_primitive_constructor() throws Exception {
+ final HashSet hashSet = mock(HashSet.class);
+ final HashMap hashMap = mock(HashMap.class);
+
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(PrimitiveConstructor.class, hashSet, hashMap);
+ final Constructor> constructor = resolver.resolveConstructor();
+ final Object[] arguments = resolver.resolveArguments();
+ constructor.newInstance(arguments);
+ }
+
+ @Test
+ public void is_resolvable_should_return_true_if_type_has_single_parameterized_contructor() throws Exception {
+ Observer observer = mock(Observer.class);
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(OneConstructor.class, observer);
+
+ assertThat(resolver.isResolvable()).isTrue();
+ }
+
+ @Test
+ public void is_resolvable_should_return_true_if_type_has_parameterized_contructor() throws Exception {
+ final Observer observer = mock(Observer.class);
+ final HashMap hashMap = mock(HashMap.class);
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(MultipleConstructor.class, observer, hashMap);
+
+ assertThat(resolver.isResolvable()).isTrue();
+ }
+
+ @Test
+ public void is_resolvable_should_return_true_with_vararg_constructor() throws Exception {
+ Observer[] vararg = new Observer[] { };
+ String string = "";
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(VarargConstructor.class, vararg, string);
+
+ assertThat(resolver.isResolvable()).isTrue();
+ }
+
+ @Test
+ public void is_resolvable_should_return_true_when_match_is_not_possible_on_given_type() throws Exception {
+ final HashMap hashMap = mock(HashMap.class);
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(MultipleConstructor.class, hashMap);
+
+ assertThat(resolver.isResolvable()).isTrue();
+ }
+
+ @Test
+ public void is_resolvable_should_return_true_when_types_are_wrappers() throws Exception {
+ final HashSet hashSet = mock(HashSet.class);
+ final HashMap hashMap = mock(HashMap.class);
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(WrapperConstructor.class, hashSet, hashMap);
+
+ assertThat(resolver.isResolvable()).isTrue();
+ }
+
+ @Test
+ public void is_resolvable_should_return_true_when_types_are_primitives() throws Exception {
+ final HashSet hashSet = mock(HashSet.class);
+ final HashMap hashMap = mock(HashMap.class);
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(PrimitiveConstructor.class, hashSet, hashMap);
+
+ assertThat(resolver.isResolvable()).isTrue();
+ }
+
+ @Test
+ public void is_resolvable_should_fail_if_no_parameterized_constructor_found() throws Exception {
+ try {
+ buildBiggestConstructorResolver(NoArgConstructor.class).isResolvable();
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("no parameterized constructor").contains("NoArgConstructor");
+ }
+ }
+
+ @Test
+ public void is_resolvable_should_fail_if_type_has_default_contructor() throws Exception {
+ try {
+ buildBiggestConstructorResolver(DefaultConstructor.class).isResolvable();
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("no parameterized constructor").contains("DefaultConstructor");
+ }
+ }
+
+ @Test
+ public void resolve_constructor_should_return_single_parameterized_constructor() throws Exception {
+ Observer observer = mock(Observer.class);
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(OneConstructor.class, observer);
+
+ final Constructor> constructor = resolver.resolveConstructor();
+ assertThat(constructor).isNotNull();
+ assertThat(constructor.getParameterTypes()).isNotNull().hasSize(1);
+ }
+
+ @Test
+ public void resolve_constructor_should_return_biggest_constructor() throws Exception {
+ final Observer observer = mock(Observer.class);
+ final HashMap hashMap = mock(HashMap.class);
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(MultipleConstructor.class, observer, hashMap);
+
+ final Constructor> constructor = resolver.resolveConstructor();
+ assertThat(constructor).isNotNull();
+ assertThat(constructor.getParameterTypes()).isNotNull().hasSize(2);
+ }
+
+ @Test
+ public void resolve_constructor_should_return_vararg_constructor() throws Exception {
+ Observer[] vararg = new Observer[] { };
+ String string = "";
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(VarargConstructor.class, vararg, string);
+
+ final Constructor> constructor = resolver.resolveConstructor();
+ assertThat(constructor).isNotNull();
+ assertThat(constructor.getParameterTypes()).isNotNull().hasSize(2);
+ }
+
+ @Test
+ public void resolve_constructor_should_fail_if_no_parameterized_constructor_found() throws Exception {
+ try {
+ buildBiggestConstructorResolver(NoArgConstructor.class).resolveConstructor();
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("no parameterized constructor").contains("NoArgConstructor");
+ }
+ }
+
+ @Test
+ public void resolve_constructor_should_fail_if_type_has_default_contructor() throws Exception {
+ try {
+ buildBiggestConstructorResolver(DefaultConstructor.class).resolveConstructor();
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("no parameterized constructor").contains("DefaultConstructor");
+ }
+ }
+
+ @Test
+ public void resolve_arguments_should_return_one_matching_object() throws Exception {
+ Observer observer = mock(Observer.class);
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(OneConstructor.class, observer);
+
+ assertThat(resolver.resolveArguments()).isNotNull().hasSize(1).containsExactly(observer);
+ }
+
+ @Test
+ public void resolve_arguments_should_return_object_matching_given_types() throws Exception {
+ final Observer observer = mock(Observer.class);
+ final HashMap hashMap = mock(HashMap.class);
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(MultipleConstructor.class, hashMap, observer);
+
+ assertThat(resolver.resolveArguments()).isNotNull().hasSize(2).containsExactly(observer, hashMap);
+ }
+
+ @Test
+ public void resolve_arguments_should_return_vararg_matching_given_types() throws Exception {
+ Observer[] vararg = new Observer[] { };
+ String string = "";
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(VarargConstructor.class, vararg, string);
+
+ assertThat(resolver.resolveArguments()).isNotNull().hasSize(2).containsExactly(string, vararg);
+ }
+
+ @Test
+ public void resolve_arguments_should_return_null_when_match_is_not_possible_on_given_type() throws Exception {
+ final HashMap hashMap = mock(HashMap.class);
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(MultipleConstructor.class, hashMap);
+
+ assertThat(resolver.resolveArguments()).isNotNull().hasSize(2).containsExactly(null, hashMap);
+ }
+
+ @Test
+ public void resolve_arguments_should_return_null_when_types_are_wrappers() throws Exception {
+ final HashSet hashSet = mock(HashSet.class);
+ final HashMap hashMap = mock(HashMap.class);
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(WrapperConstructor.class, hashSet, hashMap);
+
+ assertThat(resolver.resolveArguments()).isNotNull().hasSize(3).containsExactly(hashSet, hashMap, null);
+ }
+
+ @Test
+ public void resolve_arguments_should_return_null_when_types_are_primitives() throws Exception {
+ final HashSet hashSet = mock(HashSet.class);
+ final HashMap hashMap = mock(HashMap.class);
+ final BiggestConstructorResolver resolver = buildBiggestConstructorResolver(PrimitiveConstructor.class, hashSet, hashMap);
+
+ assertThat(resolver.resolveArguments()).isNotNull().hasSize(3).containsExactly(hashSet, hashMap, null);
+ }
+
+ protected BiggestConstructorResolver buildBiggestConstructorResolver(Class> type, Object... mocks) {
+ return new BiggestConstructorResolver(type, new HashSet(Arrays.asList(mocks)));
+ }
+
+ private static class NoArgConstructor {
+ NoArgConstructor() {
+ }
+ }
+
+ public static class DefaultConstructor {
+ }
+
+ private static class OneConstructor {
+ public OneConstructor(Observer observer) {
+ }
+ }
+
+ static class MultipleConstructor extends OneConstructor {
+ Observer observer;
+ Map map;
+
+ public MultipleConstructor(Observer observer) {
+ this(observer, null);
+ }
+
+ public MultipleConstructor(Observer observer, Map map) {
+ super(observer);
+ this.observer = observer;
+ this.map = map;
+ }
+
+ public MultipleConstructor(Observer observer, boolean nonMockable) {
+ super(observer);
+ this.observer = observer;
+ }
+ }
+
+ private static class VarargConstructor {
+ String whatever;
+ Observer[] observers;
+
+ VarargConstructor(String whatever, Observer... observers) {
+ this.whatever = whatever;
+ this.observers = observers;
+ }
+ }
+
+ static class WrapperConstructor {
+ Set set;
+ Map map;
+ Boolean bool;
+
+ WrapperConstructor(Set set, Map map, Boolean bool) {
+ this.set = set;
+ this.map = map;
+ this.bool = bool;
+ }
+ }
+
+ static class PrimitiveConstructor {
+ Set set;
+ Map map;
+ boolean bool;
+
+ PrimitiveConstructor(Set set, Map map, boolean bool) {
+ this.set = set;
+ this.map = map;
+ this.bool = bool;
+ }
+ }
+
+}
diff --git a/src/test/java/org/mockito/internal/util/reflection/FieldInitializerTest.java b/src/test/java/org/mockito/internal/util/reflection/FieldInitializerTest.java
index 8fbe6ee0b2..122b00e052 100644
--- a/src/test/java/org/mockito/internal/util/reflection/FieldInitializerTest.java
+++ b/src/test/java/org/mockito/internal/util/reflection/FieldInitializerTest.java
@@ -4,21 +4,26 @@
*/
package org.mockito.internal.util.reflection;
-import static org.junit.Assert.*;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.given;
-import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
+import java.util.HashSet;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.exceptions.base.MockitoException;
-import org.mockito.internal.util.reflection.FieldInitializer.ConstructorArgumentResolver;
-
-
+@SuppressWarnings("unchecked")
public class FieldInitializerTest {
private StaticClass alreadyInstantiated = new StaticClass();
@@ -30,60 +35,72 @@ public class FieldInitializerTest {
private AbstractStaticClass abstractType;
private Interface interfaceType;
private InnerClassType innerClassType;
+ private EnumType enumType;
private AbstractStaticClass instantiatedAbstractType = new ConcreteStaticClass();
private Interface instantiatedInterfaceType = new ConcreteStaticClass();
private InnerClassType instantiatedInnerClassType = new InnerClassType();
+ private EnumType initializedEnumType = EnumType.INITIALIZED;
@Test
public void should_keep_same_instance_if_field_initialized() throws Exception {
final StaticClass backupInstance = alreadyInstantiated;
- FieldInitializer fieldInitializer = new FieldInitializer(this, field("alreadyInstantiated"));
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+
+ FieldInitializer fieldInitializer = new FieldInitializer(this, field("alreadyInstantiated"), resolver);
FieldInitializationReport report = fieldInitializer.initialize();
assertSame(backupInstance, report.fieldInstance());
assertFalse(report.fieldWasInitialized());
- assertFalse(report.fieldWasInitializedUsingContructorArgs());
}
@Test
public void should_instantiate_field_when_type_has_no_constructor() throws Exception {
- FieldInitializer fieldInitializer = new FieldInitializer(this, field("noConstructor"));
+ final ConstructorResolver resolver = createNoArgsResolverMock(StaticClass.class);
+ FieldInitializer fieldInitializer = new FieldInitializer(this, field("noConstructor"), resolver);
FieldInitializationReport report = fieldInitializer.initialize();
assertNotNull(report.fieldInstance());
assertTrue(report.fieldWasInitialized());
- assertFalse(report.fieldWasInitializedUsingContructorArgs());
}
@Test
public void should_instantiate_field_with_default_constructor() throws Exception {
- FieldInitializer fieldInitializer = new FieldInitializer(this, field("defaultConstructor"));
+ final ConstructorResolver resolver = createNoArgsResolverMock(StaticClassWithDefaultConstructor.class);
+ FieldInitializer fieldInitializer = new FieldInitializer(this, field("defaultConstructor"), resolver);
FieldInitializationReport report = fieldInitializer.initialize();
assertNotNull(report.fieldInstance());
assertTrue(report.fieldWasInitialized());
- assertFalse(report.fieldWasInitializedUsingContructorArgs());
}
@Test
public void should_instantiate_field_with_private_default_constructor() throws Exception {
- FieldInitializer fieldInitializer = new FieldInitializer(this, field("privateDefaultConstructor"));
+ final ConstructorResolver resolver = createNoArgsResolverMock(StaticClassWithPrivateDefaultConstructor.class);
+ FieldInitializer fieldInitializer = new FieldInitializer(this, field("privateDefaultConstructor"), resolver);
FieldInitializationReport report = fieldInitializer.initialize();
assertNotNull(report.fieldInstance());
assertTrue(report.fieldWasInitialized());
- assertFalse(report.fieldWasInitializedUsingContructorArgs());
}
- @Test(expected = MockitoException.class)
- public void should_fail_to_instantiate_field_if_no_default_constructor() throws Exception {
- FieldInitializer fieldInitializer = new FieldInitializer(this, field("noDefaultConstructor"));
- fieldInitializer.initialize();
+ @Test
+ public void should_not_instantiate_field_if_resolver_says_field_is_not_resolvable() throws Exception {
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ given(resolver.isResolvable()).willReturn(false);
+
+ FieldInitializer fieldInitializer = new FieldInitializer(this, field("noDefaultConstructor"), resolver);
+ FieldInitializationReport report = fieldInitializer.initialize();
+
+ assertThat(report).isNotNull();
+ assertThat(report.fieldIsInitialized()).isFalse();
+ assertThat(report.fieldInstance()).isNull();
+ assertThat(report.fieldWasInitialized()).isFalse();
}
@Test
public void should_fail_to_instantiate_field_if_default_constructor_throws_exception() throws Exception {
- FieldInitializer fieldInitializer = new FieldInitializer(this, field("throwingExDefaultConstructor"));
+ final ConstructorResolver resolver = createNoArgsResolverMock(StaticClassThrowingExceptionDefaultConstructor.class);
+ FieldInitializer fieldInitializer = new FieldInitializer(this, field("throwingExDefaultConstructor"), resolver);
try {
fieldInitializer.initialize();
fail();
@@ -94,24 +111,54 @@ public void should_fail_to_instantiate_field_if_default_constructor_throws_excep
}
}
+ @Test
+ public void should_fail_to_instantiate_field_if_resolver_throws_expcetion() throws Exception {
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ given(resolver.isResolvable()).willThrow(new MockitoException("resolver fails"));
+
+ try {
+ new FieldInitializer(this, field("noDefaultConstructor"), resolver).initialize();
+ fail();
+ } catch (MockitoException e) {
+ assertThat(e.getMessage()).contains("noDefaultConstructor").contains("resolver fails");
+ }
+ }
+
+ @Test
+ public void should_fail_if_an_argument_instance_type_do_not_match_wanted_type() throws Exception {
+ final ConstructorResolver resolver = createParameterizedResolverMock();
+ given(resolver.resolveArguments()).willReturn(new Object[] {new HashSet()});
+
+ try {
+ new FieldInitializer(this, field("noDefaultConstructor"), resolver).initialize();
+ fail();
+ } catch (MockitoException e) {
+ assertThat(e.getMessage()).contains("ConstructorResolver").contains("incorrect types");
+ }
+ }
+
@Test(expected = MockitoException.class)
public void should_fail_for_abstract_field() throws Exception {
- new FieldInitializer(this, field("abstractType"));
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ new FieldInitializer(this, field("abstractType"), resolver);
}
@Test
public void should_not_fail_if_abstract_field_is_instantiated() throws Exception {
- new FieldInitializer(this, field("instantiatedAbstractType"));
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ new FieldInitializer(this, field("instantiatedAbstractType"), resolver);
}
@Test(expected = MockitoException.class)
public void should_fail_for_interface_field() throws Exception {
- new FieldInitializer(this, field("interfaceType"));
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ new FieldInitializer(this, field("interfaceType"), resolver);
}
@Test
public void should_not_fail_if_interface_field_is_instantiated() throws Exception {
- new FieldInitializer(this, field("instantiatedInterfaceType"));
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ new FieldInitializer(this, field("instantiatedInterfaceType"), resolver);
}
@Test(expected = MockitoException.class)
@@ -126,7 +173,8 @@ class TheTestWithLocalType {
TheTestWithLocalType testWithLocalType = new TheTestWithLocalType();
// when
- new FieldInitializer(testWithLocalType, testWithLocalType.getClass().getDeclaredField("field"));
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ new FieldInitializer(testWithLocalType, testWithLocalType.getClass().getDeclaredField("field"), resolver);
}
@Test
@@ -141,24 +189,37 @@ class TheTestWithLocalType {
TheTestWithLocalType testWithLocalType = new TheTestWithLocalType();
// when
- new FieldInitializer(testWithLocalType, testWithLocalType.getClass().getDeclaredField("field"));
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ new FieldInitializer(testWithLocalType, testWithLocalType.getClass().getDeclaredField("field"), resolver);
}
@Test(expected = MockitoException.class)
public void should_fail_for_inner_class_field() throws Exception {
- new FieldInitializer(this, field("innerClassType"));
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ new FieldInitializer(this, field("innerClassType"), resolver);
}
@Test
public void should_not_fail_if_inner_class_field_is_instantiated() throws Exception {
- new FieldInitializer(this, field("instantiatedInnerClassType"));
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ new FieldInitializer(this, field("instantiatedInnerClassType"), resolver);
+ }
+
+ @Test(expected = MockitoException.class)
+ public void should_fail_for_enum_field() throws Exception {
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ new FieldInitializer(this, field("enumType"), resolver);
}
@Test
- public void can_instantiate_class_with_parameterized_constructor() throws Exception {
- ConstructorArgumentResolver resolver = given(mock(ConstructorArgumentResolver.class).resolveTypeInstances(any(Class.class)))
- .willReturn(new Object[]{null}).getMock();
+ public void should_not_fail_if_enum_field_is_instantiated() throws Exception {
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ new FieldInitializer(this, field("initializedEnumType"), resolver);
+ }
+ @Test
+ public void can_instantiate_class_with_parameterized_constructor() throws Exception {
+ final ConstructorResolver resolver = createParameterizedResolverMock();
new FieldInitializer(this, field("noDefaultConstructor"), resolver).initialize();
assertNotNull(noDefaultConstructor);
@@ -168,6 +229,23 @@ private Field field(String fieldName) throws NoSuchFieldException {
return this.getClass().getDeclaredField(fieldName);
}
+ private ConstructorResolver createNoArgsResolverMock(Class> type) throws NoSuchMethodException {
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ final Constructor constructor = type.getDeclaredConstructor();
+ given(resolver.resolveConstructor()).willReturn(constructor);
+ given(resolver.isResolvable()).willReturn(true);
+ return resolver;
+ }
+
+ private ConstructorResolver createParameterizedResolverMock() throws NoSuchMethodException {
+ final ConstructorResolver resolver = mock(ConstructorResolver.class);
+ final Constructor constructor = StaticClassWithoutDefaultConstructor.class.getDeclaredConstructor(String.class);
+ given(resolver.resolveConstructor()).willReturn(constructor);
+ given(resolver.isResolvable()).willReturn(true);
+ given(resolver.resolveArguments()).willReturn(new Object[]{null});
+ return resolver;
+ }
+
static class StaticClass {
}
@@ -204,4 +282,8 @@ class InnerClassType {
InnerClassType() { }
}
+ enum EnumType {
+ INITIALIZED
+ }
+
}
diff --git a/src/test/java/org/mockito/internal/util/reflection/LenientNoArgsConstructorResolverTest.java b/src/test/java/org/mockito/internal/util/reflection/LenientNoArgsConstructorResolverTest.java
new file mode 100644
index 0000000000..9d2f97f721
--- /dev/null
+++ b/src/test/java/org/mockito/internal/util/reflection/LenientNoArgsConstructorResolverTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.internal.util.reflection.ConstructorResolver.LenientNoArgsConstructorResolver;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class LenientNoArgsConstructorResolverTest extends NoArgsConstructorResolverTest {
+
+ @Override
+ public void is_resolvable_should_fail_if_no_no_args_constructor_found() throws Exception {
+ // This test is not relevant for LenientNoArgsConstructorResolver
+ }
+
+ @Test
+ public void is_resolvable_should_return_false_if_no_no_args_constructor_found() throws Exception {
+ final LenientNoArgsConstructorResolver resolver = buildNoArgsConstructorResolver(OneConstructor.class);
+
+ assertThat(resolver.isResolvable()).isFalse();
+ }
+
+ @Override
+ public void is_resolvable_should_fail_with_vararg_constructor() throws Exception {
+ // This test is not relevant for LenientNoArgsConstructorResolver
+ }
+
+ @Test
+ public void is_resolvable_should_return_false_with_vararg_constructor() throws Exception {
+ final LenientNoArgsConstructorResolver resolver = buildNoArgsConstructorResolver(VarargConstructor.class);
+
+ assertThat(resolver.isResolvable()).isFalse();
+ }
+
+ @Override
+ protected LenientNoArgsConstructorResolver buildNoArgsConstructorResolver(Class> type) {
+ return new LenientNoArgsConstructorResolver(type);
+ }
+
+}
diff --git a/src/test/java/org/mockito/internal/util/reflection/NoArgsConstructorResolverTest.java b/src/test/java/org/mockito/internal/util/reflection/NoArgsConstructorResolverTest.java
new file mode 100644
index 0000000000..7d4a85eda8
--- /dev/null
+++ b/src/test/java/org/mockito/internal/util/reflection/NoArgsConstructorResolverTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Constructor;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.util.reflection.ConstructorResolver.NoArgsConstructorResolver;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class NoArgsConstructorResolverTest {
+
+ @Test
+ public void type_should_be_instantiable_if_it_has_no_args_contructor() throws Exception {
+ final NoArgsConstructorResolver resolver = buildNoArgsConstructorResolver(NoArgConstructor.class);
+ final Constructor> constructor = resolver.resolveConstructor();
+ final Object[] arguments = resolver.resolveArguments();
+ final Object newInstance = constructor.newInstance(arguments);
+
+ assertThat(newInstance).isNotNull().isInstanceOf(NoArgConstructor.class);
+ }
+
+ @Test
+ public void type_should_be_instantiable_if_it_has_default_contructor() throws Exception {
+ final NoArgsConstructorResolver resolver = buildNoArgsConstructorResolver(DefaultConstructor.class);
+ final Constructor> constructor = resolver.resolveConstructor();
+ final Object[] arguments = resolver.resolveArguments();
+ final Object newInstance = constructor.newInstance(arguments);
+
+ assertThat(newInstance).isNotNull().isInstanceOf(DefaultConstructor.class);
+ }
+
+ @Test
+ public void is_resolvable_should_return_true_if_type_has_no_args_contructor() throws Exception {
+ final NoArgsConstructorResolver resolver = buildNoArgsConstructorResolver(NoArgConstructor.class);
+
+ assertThat(resolver.isResolvable()).isTrue();
+ }
+
+ @Test
+ public void is_resolvable_should_return_true_if_type_has_default_contructor() throws Exception {
+ final NoArgsConstructorResolver resolver = buildNoArgsConstructorResolver(DefaultConstructor.class);
+
+ assertThat(resolver.isResolvable()).isTrue();
+ }
+
+ @Test
+ public void is_resolvable_should_fail_if_no_no_args_constructor_found() throws Exception {
+ try {
+ buildNoArgsConstructorResolver(OneConstructor.class).isResolvable();
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("no default constructor").contains("OneConstructor");
+ }
+ }
+
+ @Test
+ public void is_resolvable_should_fail_with_vararg_constructor() throws Exception {
+ try {
+ buildNoArgsConstructorResolver(VarargConstructor.class).isResolvable();
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("no default constructor").contains("VarargConstructor");
+ }
+ }
+
+ @Test
+ public void resolve_constructor_should_return_no_args_constructor() throws Exception {
+ final NoArgsConstructorResolver resolver = buildNoArgsConstructorResolver(NoArgConstructor.class);
+
+ final Constructor> constructor = resolver.resolveConstructor();
+ assertThat(constructor).isNotNull();
+ assertThat(constructor.getParameterTypes()).isNotNull().isEmpty();
+ }
+
+ @Test
+ public void resolve_constructor_should_return_default_constructor() throws Exception {
+ final NoArgsConstructorResolver resolver = buildNoArgsConstructorResolver(DefaultConstructor.class);
+
+ final Constructor> constructor = resolver.resolveConstructor();
+ assertThat(constructor).isNotNull();
+ assertThat(constructor.getParameterTypes()).isNotNull().isEmpty();
+ }
+
+ @Test
+ public void resolve_constructor_should_fail_if_no_no_args_constructor_found() throws Exception {
+ try {
+ buildNoArgsConstructorResolver(OneConstructor.class).resolveConstructor();
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("no default constructor").contains("OneConstructor");
+ }
+ }
+
+ @Test
+ public void resolve_constructor_should_fail_with_vararg_constructor() throws Exception {
+ try {
+ buildNoArgsConstructorResolver(VarargConstructor.class).resolveConstructor();
+ fail();
+ } catch (MockitoException me) {
+ assertThat(me.getMessage()).contains("no default constructor").contains("VarargConstructor");
+ }
+ }
+
+ @Test
+ public void resolve_arguments_should_return_empty_object_array() throws Exception {
+ final NoArgsConstructorResolver resolver = buildNoArgsConstructorResolver(NoArgConstructor.class);
+
+ assertThat(resolver.resolveArguments()).isNotNull().isEmpty();
+ }
+
+ protected NoArgsConstructorResolver buildNoArgsConstructorResolver(Class> type) {
+ return new NoArgsConstructorResolver(type);
+ }
+
+ private static class NoArgConstructor {
+ NoArgConstructor() {
+ }
+ }
+
+ public static class DefaultConstructor {
+ }
+
+ static class OneConstructor {
+ public OneConstructor(String whatever) {
+ }
+ }
+
+ static class VarargConstructor {
+ VarargConstructor(String... whatever) {
+ }
+ }
+
+}
diff --git a/src/test/java/org/mockito/internal/util/reflection/ParameterizedConstructorInstantiatorTest.java b/src/test/java/org/mockito/internal/util/reflection/ParameterizedConstructorInstantiatorTest.java
deleted file mode 100644
index 1ffa6cfbf3..0000000000
--- a/src/test/java/org/mockito/internal/util/reflection/ParameterizedConstructorInstantiatorTest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (c) 2007 Mockito contributors
- * This program is made available under the terms of the MIT License.
- */
-package org.mockito.internal.util.reflection;
-
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.mockito.BDDMockito.given;
-import static org.mockito.Mockito.mock;
-
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.util.Map;
-import java.util.Observer;
-import java.util.Set;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Matchers;
-import org.mockito.Mock;
-import org.mockito.exceptions.base.MockitoException;
-import org.mockito.internal.util.reflection.FieldInitializer.ConstructorArgumentResolver;
-import org.mockito.internal.util.reflection.FieldInitializer.ParameterizedConstructorInstantiator;
-import org.mockito.junit.MockitoJUnitRunner;
-
-
-@SuppressWarnings("unchecked")
-@RunWith(MockitoJUnitRunner.class)
-public class ParameterizedConstructorInstantiatorTest {
-
- private Set> whateverForNow;
- private OneConstructor withOneConstructor;
- private MultipleConstructor withMultipleConstructor;
- private NoArgConstructor withNoArgConstructor;
- private ThrowingConstructor withThrowingConstructor;
- private VarargConstructor withVarargConstructor;
-
- @After
- public void ensure_instances_to_create_are_null() {
- withMultipleConstructor = null;
- withOneConstructor = null;
- withNoArgConstructor = null;
- withThrowingConstructor = null;
- withVarargConstructor = null;
- }
-
- @Mock private ConstructorArgumentResolver resolver;
-
- @Test
- public void should_be_created_with_an_argument_resolver() throws Exception {
- new ParameterizedConstructorInstantiator(this, field("whateverForNow"), resolver);
- }
-
- @Test
- public void should_fail_if_no_parameterized_constructor_found___excluding_inner_and_others_kind_of_types() throws Exception {
- try {
- new ParameterizedConstructorInstantiator(this, field("withNoArgConstructor"), resolver).instantiate();
- fail();
- } catch (MockitoException me) {
- assertThat(me.getMessage()).contains("no parameterized constructor").contains("withNoArgConstructor").contains("NoArgConstructor");
- }
- }
-
- @Test
- 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(Matchers.[]>anyVararg())).willReturn(new Object[]{ observer, map });
-
- new ParameterizedConstructorInstantiator(this, field("withMultipleConstructor"), resolver).instantiate();
-
- assertNotNull(withMultipleConstructor);
- assertNotNull(withMultipleConstructor.observer);
- assertNotNull(withMultipleConstructor.map);
- }
-
- @Test
- public void should_fail_if_an_argument_instance_type_do_not_match_wanted_type() throws Exception {
- Observer observer = mock(Observer.class);
- Set> wrongArg = mock(Set.class);
- given(resolver.resolveTypeInstances(Matchers.[]>anyVararg())).willReturn(new Object[]{ observer, wrongArg });
-
- try {
- new ParameterizedConstructorInstantiator(this, field("withMultipleConstructor"), resolver).instantiate();
- fail();
- } catch (MockitoException e) {
- assertThat(e.getMessage()).contains("argResolver").contains("incorrect types");
- }
- }
-
- @Test
- public void should_report_failure_if_constructor_throws_exception() throws Exception {
- given(resolver.resolveTypeInstances(Matchers.[]>anyVararg())).willReturn(new Object[]{ null });
-
- try {
- new ParameterizedConstructorInstantiator(this, field("withThrowingConstructor"), resolver).instantiate();
- fail();
- } catch (MockitoException e) {
- assertThat(e.getMessage()).contains("constructor").contains("raised an exception");
- }
- }
-
- @Test
- public void should_instantiate_type_with_vararg_constructor() throws Exception {
- Observer[] vararg = new Observer[] { };
- given(resolver.resolveTypeInstances(Matchers.[]>anyVararg())).willReturn(new Object[]{ "", vararg});
-
- new ParameterizedConstructorInstantiator(this, field("withVarargConstructor"), resolver).instantiate();
-
- assertNotNull(withVarargConstructor);
- }
-
- private Field field(String fieldName) throws NoSuchFieldException {
- Field field = this.getClass().getDeclaredField(fieldName);
- field.setAccessible(true);
- return field;
- }
-
- private static class NoArgConstructor {
- NoArgConstructor() { }
- }
-
- private static class OneConstructor {
- public OneConstructor(Observer observer) { }
- }
-
- private static class ThrowingConstructor {
- public ThrowingConstructor(Observer observer) throws IOException { throw new IOException(); }
- }
-
- private static class MultipleConstructor extends OneConstructor {
- Observer observer;
- Map map;
-
- public MultipleConstructor(Observer observer) { this(observer, null); }
- public MultipleConstructor(Observer observer, Map map) {
- super(observer);
- this.observer = observer;
- this.map = map;
- }
- }
-
- private static class VarargConstructor {
- VarargConstructor(String whatever, Observer... observers) { }
- }
-}
diff --git a/src/test/java/org/mockito/internal/util/reflection/StrictBiggestConstructorResolverTest.java b/src/test/java/org/mockito/internal/util/reflection/StrictBiggestConstructorResolverTest.java
new file mode 100644
index 0000000000..f73d2b9746
--- /dev/null
+++ b/src/test/java/org/mockito/internal/util/reflection/StrictBiggestConstructorResolverTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.internal.util.reflection.ConstructorResolver.StrictBiggestConstructorResolver;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@SuppressWarnings("unchecked")
+@RunWith(MockitoJUnitRunner.class)
+public class StrictBiggestConstructorResolverTest extends BiggestConstructorResolverTest {
+
+ @Override
+ public void is_resolvable_should_return_true_when_match_is_not_possible_on_given_type() throws Exception {
+ // This test is not relevant for LenientNoArgsConstructorResolver
+ }
+
+ @Test
+ public void is_resolvable_should_return_false_when_match_is_not_possible_on_given_type() throws Exception {
+ final HashMap hashMap = mock(HashMap.class);
+ final StrictBiggestConstructorResolver resolver = buildBiggestConstructorResolver(MultipleConstructor.class, hashMap);
+
+ assertThat(resolver.isResolvable()).isFalse();
+ }
+
+ @Override
+ public void is_resolvable_should_return_true_when_types_are_wrappers() throws Exception {
+ // This test is not relevant for LenientNoArgsConstructorResolver
+ }
+
+ @Test
+ public void is_resolvable_should_return_false_when_types_are_wrappers() throws Exception {
+ final HashSet hashSet = mock(HashSet.class);
+ final HashMap hashMap = mock(HashMap.class);
+ final StrictBiggestConstructorResolver resolver = buildBiggestConstructorResolver(WrapperConstructor.class, hashSet, hashMap);
+
+ assertThat(resolver.isResolvable()).isFalse();
+ }
+
+ @Override
+ public void is_resolvable_should_return_true_when_types_are_primitives() throws Exception {
+ // This test is not relevant for LenientNoArgsConstructorResolver
+ }
+
+ @Test
+ public void is_resolvable_should_return_false_when_types_are_primitives() throws Exception {
+ final HashSet hashSet = mock(HashSet.class);
+ final HashMap hashMap = mock(HashMap.class);
+ final StrictBiggestConstructorResolver resolver = buildBiggestConstructorResolver(PrimitiveConstructor.class, hashSet, hashMap);
+
+ assertThat(resolver.isResolvable()).isFalse();
+ }
+
+ @Override
+ protected StrictBiggestConstructorResolver buildBiggestConstructorResolver(Class> type, Object... mocks) {
+ return new StrictBiggestConstructorResolver(type, new HashSet(Arrays.asList(mocks)));
+ }
+
+}
diff --git a/src/test/java/org/mockitousage/annotation/InjectingAnnotationEngineByConstructorTest.java b/src/test/java/org/mockitousage/annotation/InjectingAnnotationEngineByConstructorTest.java
new file mode 100644
index 0000000000..67dbc5f944
--- /dev/null
+++ b/src/test/java/org/mockitousage/annotation/InjectingAnnotationEngineByConstructorTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019 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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.internal.configuration.InjectingAnnotationEngine;
+
+public class InjectingAnnotationEngineByConstructorTest {
+
+ public static class MockedDao {
+ void assertInjection() {
+ fail("should be mocked");
+ }
+ }
+
+ public static class ByConstructorSpiedService {
+ final MockedDao mockedDao;
+ ByConstructorSpiedService(MockedDao mockedDao) {
+ this.mockedDao = mockedDao;
+ }
+ void assertInjection() {
+ assertNotNull(mockedDao);
+ mockedDao.assertInjection();
+ }
+ }
+
+ public static class ByConstructorTestedService {
+ final MockedDao mockedDao;
+ final ByConstructorSpiedService byConstructorSpiedService;
+ ByConstructorTestedService(MockedDao mockedDao, ByConstructorSpiedService byConstructorSpiedService) {
+ this.mockedDao = mockedDao;
+ this.byConstructorSpiedService = byConstructorSpiedService;
+ }
+ void assertInjection() {
+ assertNotNull(mockedDao);
+ mockedDao.assertInjection();
+ assertNotNull(byConstructorSpiedService);
+ byConstructorSpiedService.assertInjection();
+ }
+ }
+
+ @InjectMocks
+ private ByConstructorTestedService byConstructorTestedService;
+
+ @InjectMocks
+ @Spy
+ private ByConstructorSpiedService byConstructorSpiedService;
+
+ @Mock
+ private MockedDao mockedDao;
+
+ @Test
+ public void should_inject_mocks_and_spies_by_constructor() {
+ new InjectingAnnotationEngine().process(getClass(), this);
+ assertNotNull(byConstructorTestedService);
+ byConstructorTestedService.assertInjection();
+ }
+
+ @Test
+ public void injected_mocks_and_spies_should_be_of_same_instance() {
+ new InjectingAnnotationEngine().process(getClass(), this);
+ assertNotNull(byConstructorTestedService);
+ byConstructorTestedService.assertInjection();
+ assertEquals(byConstructorSpiedService, byConstructorTestedService.byConstructorSpiedService);
+ assertEquals(mockedDao, byConstructorTestedService.mockedDao);
+ assertEquals(mockedDao, byConstructorTestedService.byConstructorSpiedService.mockedDao);
+ }
+
+}
diff --git a/src/test/java/org/mockitousage/annotation/InjectingAnnotationEngineByPropertyTest.java b/src/test/java/org/mockitousage/annotation/InjectingAnnotationEngineByPropertyTest.java
new file mode 100644
index 0000000000..3b17585f83
--- /dev/null
+++ b/src/test/java/org/mockitousage/annotation/InjectingAnnotationEngineByPropertyTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.internal.configuration.InjectingAnnotationEngine;
+
+public class InjectingAnnotationEngineByPropertyTest {
+
+ public static class MockedDao {
+ void assertInjection() {
+ fail("should be mocked");
+ }
+ }
+
+ public static class ByPropertySpiedService {
+ MockedDao mockedDao;
+ void assertInjection() {
+ assertNotNull(mockedDao);
+ mockedDao.assertInjection();
+ }
+ }
+
+ public static class ByPropertyTestedService {
+ MockedDao mockedDao;
+ ByPropertySpiedService byPropertySpiedService;
+ void assertInjection() {
+ assertNotNull(mockedDao);
+ mockedDao.assertInjection();
+ assertNotNull(byPropertySpiedService);
+ byPropertySpiedService.assertInjection();
+ }
+ }
+
+ @InjectMocks
+ private ByPropertyTestedService byPropertyTestedService;
+
+ @InjectMocks
+ @Spy
+ private ByPropertySpiedService byPropertySpiedService;
+
+ @Mock
+ private MockedDao mockedDao;
+
+ @Test
+ public void should_inject_mocks_and_spies_by_property() {
+ new InjectingAnnotationEngine().process(getClass(), this);
+ assertNotNull(byPropertyTestedService);
+ byPropertyTestedService.assertInjection();
+ }
+
+ @Test
+ public void injected_mocks_and_spies_should_be_of_same_instance() {
+ new InjectingAnnotationEngine().process(getClass(), this);
+ assertNotNull(byPropertyTestedService);
+ byPropertyTestedService.assertInjection();
+ assertEquals(byPropertySpiedService, byPropertyTestedService.byPropertySpiedService);
+ assertEquals(mockedDao, byPropertyTestedService.mockedDao);
+ assertEquals(mockedDao, byPropertyTestedService.byPropertySpiedService.mockedDao);
+ }
+
+}
diff --git a/src/test/java/org/mockitousage/annotation/InjectingAnnotationEngineCircularInjectionByConstructorTest.java b/src/test/java/org/mockitousage/annotation/InjectingAnnotationEngineCircularInjectionByConstructorTest.java
new file mode 100644
index 0000000000..d1edede23f
--- /dev/null
+++ b/src/test/java/org/mockitousage/annotation/InjectingAnnotationEngineCircularInjectionByConstructorTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockitousage.annotation;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Spy;
+import org.mockito.internal.configuration.InjectingAnnotationEngine;
+
+public class InjectingAnnotationEngineCircularInjectionByConstructorTest {
+
+ public static class TestedService {
+ final SpiedService1 service1;
+ final SpiedService2 service2;
+ public TestedService(SpiedService1 service1, SpiedService2 service2) {
+ this.service1 = service1;
+ this.service2 = service2;
+ }
+ }
+
+ public static class SpiedService1 {
+ final SpiedService2 service2;
+ public SpiedService1(SpiedService2 service2) {
+ this.service2 = service2;
+ }
+ }
+
+ public static class SpiedService2 {
+ final SpiedService1 service1;
+ public SpiedService2(SpiedService1 service) {
+ this.service1 = service;
+ }
+ }
+
+ @InjectMocks
+ private TestedService testedService;
+
+ @InjectMocks
+ @Spy
+ private SpiedService1 spiedService1;
+
+ @InjectMocks
+ @Spy
+ private SpiedService2 spiedService2;
+
+ @Test
+ public void should_fail_to_inject_circular_dependencies_by_constructor() {
+ new InjectingAnnotationEngine().process(getClass(), this);
+ Assert.assertNotNull(testedService);
+ Assert.assertNull(testedService.service1);
+ Assert.assertNull(testedService.service2);
+ Assert.assertNotNull(spiedService1);
+ Assert.assertNull(spiedService1.service2);
+ Assert.assertNotNull(spiedService2);
+ Assert.assertNull(spiedService2.service1);
+ }
+
+}
diff --git a/src/test/java/org/mockitousage/annotation/InjectingAnnotationEngineCircularInjectionByPropertyTest.java b/src/test/java/org/mockitousage/annotation/InjectingAnnotationEngineCircularInjectionByPropertyTest.java
new file mode 100644
index 0000000000..917adc7da9
--- /dev/null
+++ b/src/test/java/org/mockitousage/annotation/InjectingAnnotationEngineCircularInjectionByPropertyTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 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 static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Spy;
+import org.mockito.internal.configuration.InjectingAnnotationEngine;
+
+public class InjectingAnnotationEngineCircularInjectionByPropertyTest {
+
+ public static class SpiedService1 {
+ SpiedService2 spiedService2;
+ void assertInjection() {
+ assertNotNull(spiedService2);
+ }
+ }
+
+ public static class SpiedService2 {
+ SpiedService1 spiedService1;
+ void assertInjection() {
+ assertNotNull(spiedService1);
+ }
+ }
+
+ public static class TestedService {
+ SpiedService1 spiedService1;
+ SpiedService2 spiedService2;
+ void assertInjection() {
+ assertNotNull(spiedService1);
+ assertNotNull(spiedService2);
+ spiedService1.assertInjection();
+ spiedService2.assertInjection();
+ }
+ }
+
+ @InjectMocks
+ private TestedService testedService;
+
+ @InjectMocks
+ @Spy
+ private SpiedService1 spiedService1;
+
+ @InjectMocks
+ @Spy
+ private SpiedService2 spiedService2;
+
+ @Test
+ public void should_inject_mocks_and_spies_by_property() {
+ new InjectingAnnotationEngine().process(getClass(), this);
+ assertNotNull(testedService);
+ testedService.assertInjection();
+ }
+
+ @Test
+ public void injected_mocks_and_spies_should_be_of_same_instance() {
+ new InjectingAnnotationEngine().process(getClass(), this);
+ assertNotNull(testedService);
+ testedService.assertInjection();
+ assertEquals(spiedService1, testedService.spiedService1);
+ assertEquals(spiedService1, testedService.spiedService2.spiedService1);
+ assertEquals(spiedService2, testedService.spiedService2);
+ assertEquals(spiedService2, testedService.spiedService1.spiedService2);
+ }
+
+}