diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2018-03-18 07:24:20 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2018-03-18 07:24:20 +0000 |
commit | 11b67bf704c95c167f4f3ccedca50630ec341461 (patch) | |
tree | 69aeac615e38207debf91d5bc92bd75b89130993 | |
parent | df75c5c81811a1a84c27a32f0d645f965020e693 (diff) | |
parent | bf8c1ad2adfe37bae65749ebc46f76c72b475f32 (diff) | |
download | mockito-11b67bf704c95c167f4f3ccedca50630ec341461.tar.gz |
Snap for 4662252 from bf8c1ad2adfe37bae65749ebc46f76c72b475f32 to pi-releaseandroid-wear-9.0.0_r9android-wear-9.0.0_r8android-wear-9.0.0_r7android-wear-9.0.0_r6android-wear-9.0.0_r5android-wear-9.0.0_r4android-wear-9.0.0_r34android-wear-9.0.0_r33android-wear-9.0.0_r32android-wear-9.0.0_r31android-wear-9.0.0_r30android-wear-9.0.0_r3android-wear-9.0.0_r29android-wear-9.0.0_r28android-wear-9.0.0_r27android-wear-9.0.0_r26android-wear-9.0.0_r25android-wear-9.0.0_r24android-wear-9.0.0_r23android-wear-9.0.0_r22android-wear-9.0.0_r21android-wear-9.0.0_r20android-wear-9.0.0_r2android-wear-9.0.0_r19android-wear-9.0.0_r18android-wear-9.0.0_r17android-wear-9.0.0_r16android-wear-9.0.0_r15android-wear-9.0.0_r14android-wear-9.0.0_r13android-wear-9.0.0_r12android-wear-9.0.0_r11android-wear-9.0.0_r10android-wear-9.0.0_r1android-vts-9.0_r9android-vts-9.0_r8android-vts-9.0_r7android-vts-9.0_r6android-vts-9.0_r5android-vts-9.0_r4android-vts-9.0_r19android-vts-9.0_r18android-vts-9.0_r17android-vts-9.0_r16android-vts-9.0_r15android-vts-9.0_r14android-vts-9.0_r13android-vts-9.0_r12android-vts-9.0_r11android-vts-9.0_r10android-security-9.0.0_r76android-security-9.0.0_r75android-security-9.0.0_r74android-security-9.0.0_r73android-security-9.0.0_r72android-security-9.0.0_r71android-security-9.0.0_r70android-security-9.0.0_r69android-security-9.0.0_r68android-security-9.0.0_r67android-security-9.0.0_r66android-security-9.0.0_r65android-security-9.0.0_r64android-security-9.0.0_r63android-security-9.0.0_r62android-cts-9.0_r9android-cts-9.0_r8android-cts-9.0_r7android-cts-9.0_r6android-cts-9.0_r5android-cts-9.0_r4android-cts-9.0_r3android-cts-9.0_r20android-cts-9.0_r2android-cts-9.0_r19android-cts-9.0_r18android-cts-9.0_r17android-cts-9.0_r16android-cts-9.0_r15android-cts-9.0_r14android-cts-9.0_r13android-cts-9.0_r12android-cts-9.0_r11android-cts-9.0_r10android-cts-9.0_r1android-9.0.0_r9android-9.0.0_r8android-9.0.0_r7android-9.0.0_r61android-9.0.0_r60android-9.0.0_r6android-9.0.0_r59android-9.0.0_r58android-9.0.0_r57android-9.0.0_r56android-9.0.0_r55android-9.0.0_r54android-9.0.0_r53android-9.0.0_r52android-9.0.0_r51android-9.0.0_r50android-9.0.0_r5android-9.0.0_r49android-9.0.0_r48android-9.0.0_r3android-9.0.0_r2android-9.0.0_r18android-9.0.0_r17android-9.0.0_r10android-9.0.0_r1security-pi-releasepie-vts-releasepie-security-releasepie-s2-releasepie-release-2pie-releasepie-r2-s2-releasepie-r2-s1-releasepie-r2-releasepie-platform-releasepie-gsipie-cuttlefish-testingpie-cts-release
Change-Id: Ied773c8bf4a09e5610c303f088632bc5ddd47daf
80 files changed, 1580 insertions, 454 deletions
@@ -35,11 +35,7 @@ java_library_static { // dexmaker instead and including it causes conflicts. exclude_srcs: [ "src/main/java/org/mockito/internal/creation/bytebuddy/**/*.java", - "src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java", ], - srcs: [ - "stub/java/org/mockito/internal/invocation/DefaultInvocationFactory.java", - ] }, host: { static_libs: [ diff --git a/README.version b/README.version index 64b0bed..bbabbe6 100644 --- a/README.version +++ b/README.version @@ -1,5 +1,5 @@ URL: https://github.com/mockito/mockito -Version: v2.12.0 +Version: v2.16.0 License: Apache 2.0 Description: Mockito is a mocking framework with a clean and simple API. @@ -9,4 +9,4 @@ Dexmaker module. The source can be updated using the update_source.sh script. Local Modifications: - - Added dummy DefaultInvocationFactory to fix build + None diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 415df01..b25dae5 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -25,6 +25,7 @@ import org.mockito.plugins.MockitoPlugins; import org.mockito.quality.MockitoHint; import org.mockito.quality.Strictness; import org.mockito.session.MockitoSessionBuilder; +import org.mockito.session.MockitoSessionLogger; import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer1; import org.mockito.stubbing.OngoingStubbing; @@ -96,6 +97,8 @@ import org.mockito.verification.VerificationWithTimeout; * <a href="#40">40. (*new*) Improved productivity and cleaner tests with "stricter" Mockito (Since 2.+)</a><br/> * <a href="#41">41. (**new**) Advanced public API for framework integrations (Since 2.10.+)</a><br/> * <a href="#42">42. (**new**) New API for integrations: listening on verification start events (Since 2.11.+)</a><br/> + * <a href="#43">43. (**new**) New API for integrations: <code>MockitoSession</code> is usable by testing frameworks (Since 2.15.+)</a><br/> + * <a href="#44">44. Deprecated <code>org.mockito.plugins.InstantiatorProvider</code> as it was leaking internal API. it was replaced by <code>org.mockito.plugins.InstantiatorProvider2 (Since 2.15.4)</code></a><br/> * </b> * * <h3 id="0">0. <a class="meaningful_link" href="#mockito2" name="mockito2">Migrating to Mockito 2</a></h3> @@ -1396,7 +1399,7 @@ import org.mockito.verification.VerificationWithTimeout; * <a href="https://github.com/mockito/mockito/issues/769">issue 769</a>. * * <h3 id="41">41. <a class="meaningful_link" href="#framework_integrations_api" name="framework_integrations_api"> - * (**new**) Advanced public API for framework integrations (Since 2.10.+)</h3> + * (**new**) Advanced public API for framework integrations (Since 2.10.+)</a></h3> * * In Summer 2017 we decided that Mockito * <a href="https://www.linkedin.com/pulse/mockito-vs-powermock-opinionated-dogmatic-static-mocking-faber"> @@ -1449,7 +1452,7 @@ import org.mockito.verification.VerificationWithTimeout; * Do you have feedback? Please leave comment in <a href="https://github.com/mockito/mockito/issues/1110">issue 1110</a>. * * <h3 id="42">42. <a class="meaningful_link" href="#verifiation_started_listener" name="verifiation_started_listener"> - * (**new**) New API for integrations: listening on verification start events (Since 2.11.+)</h3> + * (**new**) New API for integrations: listening on verification start events (Since 2.11.+)</a></h3> * * Framework integrations such as <a href="https://projects.spring.io/spring-boot">Spring Boot</a> needs public API to tackle double-proxy use case * (<a href="https://github.com/mockito/mockito/issues/1191">issue 1191</a>). @@ -1467,6 +1470,42 @@ import org.mockito.verification.VerificationWithTimeout; * We found this method useful during the implementation. * </li> * </ul> + * + * <h3 id="43">43. <a class="meaningful_link" href="#mockito_session_testing_frameworks" name="mockito_session_testing_frameworks"> + * (**new**) New API for integrations: <code>MockitoSession</code> is usable by testing frameworks (Since 2.15.+)</a></h3> + * + * <p>{@link MockitoSessionBuilder} and {@link MockitoSession} were enhanced to enable reuse by testing framework + * integrations (e.g. {@link MockitoRule} for JUnit):</p> + * <ul> + * <li>{@link MockitoSessionBuilder#initMocks(Object...)} allows to pass in multiple test class instances for + * initialization of fields annotated with Mockito annotations like {@link org.mockito.Mock}. + * This method is useful for advanced framework integrations (e.g. JUnit Jupiter), when a test uses multiple, + * e.g. nested, test class instances. + * </li> + * <li>{@link MockitoSessionBuilder#name(String)} allows to pass a name from the testing framework to the + * {@link MockitoSession} that will be used for printing warnings when {@link Strictness#WARN} is used. + * </li> + * <li>{@link MockitoSessionBuilder#logger(MockitoSessionLogger)} makes it possible to customize the logger used + * for hints/warnings produced when finishing mocking (useful for testing and to connect reporting capabilities + * provided by testing frameworks such as JUnit Jupiter). + * </li> + * <li>{@link MockitoSession#setStrictness(Strictness)} allows to change the strictness of a {@link MockitoSession} + * for one-off scenarios, e.g. it enables configuring a default strictness for all tests in a class but makes it + * possible to change the strictness for a single or a few tests. + * </li> + * <li>{@link MockitoSession#finishMocking(Throwable)} was added to avoid confusion that may arise because + * there are multiple competing failures. It will disable certain checks when the supplied <em>failure</em> + * is not {@code null}. + * </li> + * </ul> + * + * <h3 id="44">44. <a class="meaningful_link" href="#mockito_instantiator_provider_deprecation" name="mockito_instantiator_provider_deprecation"> + * Deprecated <code>org.mockito.plugins.InstantiatorProvider</code> as it was leaking internal API. it was + * replaced by <code>org.mockito.plugins.InstantiatorProvider2 (Since 2.15.4)</a></h3> + * + * <p>{@link org.mockito.plugins.InstantiatorProvider} returned an internal API. Hence it was deprecated and replaced + * by {@link org.mockito.plugins.InstantiatorProvider2}. Old {@link org.mockito.plugins.InstantiatorProvider + * instantiator providers} will continue to work, but it is recommended to switch to the new API.</p> */ @SuppressWarnings("unchecked") public class Mockito extends ArgumentMatchers { @@ -1643,11 +1682,15 @@ public class Mockito extends ArgumentMatchers { * // this calls the real implementation of Foo.getSomething() * value = mock.getSomething(); * - * when(mock.getSomething()).thenReturn(fakeValue); + * doReturn(fakeValue).when(mock).getSomething(); * * // now fakeValue is returned * value = mock.getSomething(); * </code></pre> + * + * <p> + * <u>Note:</u> Stubbing partial mocks using <code>when(mock.getSomething()).thenReturn(fakeValue)</code> + * syntax will call the real method. For partial mock it's recommended to use <code>doReturn</code> syntax. */ public static final Answer<Object> CALLS_REAL_METHODS = Answers.CALLS_REAL_METHODS; @@ -2119,6 +2162,9 @@ public class Mockito extends ArgumentMatchers { * See also {@link Mockito#never()} - it is more explicit and communicates the intent well. * <p> * Stubbed invocations (if called) are also treated as interactions. + * If you want stubbed invocations automatically verified, check out {@link Strictness#STRICT_STUBS} feature + * introduced in Mockito 2.3.0. + * If you want to ignore stubs for verification, see {@link #ignoreStubs(Object...)}. * <p> * A word of <b>warning</b>: * Some users who did a lot of classic, expect-run-verify mocking tend to use <code>verifyNoMoreInteractions()</code> very often, even in every test method. diff --git a/src/main/java/org/mockito/MockitoSession.java b/src/main/java/org/mockito/MockitoSession.java index 997a4fa..9e820f2 100644 --- a/src/main/java/org/mockito/MockitoSession.java +++ b/src/main/java/org/mockito/MockitoSession.java @@ -4,6 +4,7 @@ */ package org.mockito; +import org.mockito.exceptions.misusing.PotentialStubbingProblem; import org.mockito.exceptions.misusing.UnfinishedMockingSessionException; import org.mockito.exceptions.misusing.UnnecessaryStubbingException; import org.mockito.junit.MockitoJUnitRunner; @@ -90,6 +91,20 @@ import org.mockito.session.MockitoSessionBuilder; public interface MockitoSession { /** + * Changes the strictness of this {@code MockitoSession}. + * The new strictness will be applied to operations on mocks and checks performed by {@link #finishMocking()}. + * This method is used behind the hood by {@link MockitoRule#strictness(Strictness)} method. + * In most healthy tests, this method is not needed. + * We keep it for edge cases and when you really need to change strictness in given test method. + * For use cases see Javadoc for {@link PotentialStubbingProblem} class. + * + * @param strictness new strictness for this session. + * @since 2.15.0 + */ + @Incubating + void setStrictness(Strictness strictness); + + /** * Must be invoked when the user is done with mocking for given session (test method). * It detects unused stubbings and may throw {@link UnnecessaryStubbingException} * or emit warnings ({@link MockitoHint}) depending on the {@link Strictness} level. @@ -105,8 +120,26 @@ public interface MockitoSession { * <p> * For example, see javadoc for {@link MockitoSession}. * + * @see #finishMocking(Throwable) * @since 2.7.0 */ @Incubating void finishMocking(); + + /** + * Must be invoked when the user is done with mocking for given session (test method). + * When a {@linkplain Throwable failure} is specified, certain checks are disabled to avoid + * confusion that may arise because there are multiple competing failures. Other than that, + * this method behaves exactly like {@link #finishMocking()}. + * <p> + * This method is intended to be used by framework integrations. When using MockitoSession + * directly, most users should rather use {@link #finishMocking()}. + * {@link MockitoRule} uses this method behind the hood. + * + * @param failure the exception that caused the test to fail; passing {@code null} is permitted + * @see #finishMocking() + * @since 2.15.0 + */ + @Incubating + void finishMocking(Throwable failure); } diff --git a/src/main/java/org/mockito/creation/instance/InstantiationException.java b/src/main/java/org/mockito/creation/instance/InstantiationException.java new file mode 100644 index 0000000..1cfbaba --- /dev/null +++ b/src/main/java/org/mockito/creation/instance/InstantiationException.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2016 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.creation.instance; + +import org.mockito.exceptions.base.MockitoException; + +/** + * Exception generated when {@link Instantiator#newInstance(Class)} failed. + * + * @since 2.15.4 + */ +public class InstantiationException extends MockitoException { + + /** + * @since 2.15.4 + */ + public InstantiationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/mockito/creation/instance/Instantiator.java b/src/main/java/org/mockito/creation/instance/Instantiator.java new file mode 100644 index 0000000..9ce37b5 --- /dev/null +++ b/src/main/java/org/mockito/creation/instance/Instantiator.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2016 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.creation.instance; + +/** + * Provides instances of classes. + * See more information about Mockito plugin {@link org.mockito.plugins.InstantiatorProvider2} + * + * @since 2.15.4 + */ +public interface Instantiator { + + /** + * Creates instance of given class + * + * @since 2.15.4 + */ + <T> T newInstance(Class<T> cls) throws InstantiationException; + +} diff --git a/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java b/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java index 983e066..f80e7c4 100644 --- a/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java +++ b/src/main/java/org/mockito/internal/configuration/plugins/DefaultMockitoPlugins.java @@ -4,8 +4,10 @@ */ package org.mockito.internal.configuration.plugins; +import org.mockito.internal.creation.instance.InstantiatorProvider2Adapter; import org.mockito.plugins.AnnotationEngine; import org.mockito.plugins.InstantiatorProvider; +import org.mockito.plugins.InstantiatorProvider2; import org.mockito.plugins.MockMaker; import org.mockito.plugins.MockitoPlugins; import org.mockito.plugins.PluginSwitch; @@ -24,15 +26,22 @@ class DefaultMockitoPlugins implements MockitoPlugins { DEFAULT_PLUGINS.put(PluginSwitch.class.getName(), DefaultPluginSwitch.class.getName()); DEFAULT_PLUGINS.put(MockMaker.class.getName(), "org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker"); DEFAULT_PLUGINS.put(StackTraceCleanerProvider.class.getName(), "org.mockito.internal.exceptions.stacktrace.DefaultStackTraceCleanerProvider"); - DEFAULT_PLUGINS.put(InstantiatorProvider.class.getName(), "org.mockito.internal.creation.instance.DefaultInstantiatorProvider"); + DEFAULT_PLUGINS.put(InstantiatorProvider2.class.getName(), "org.mockito.internal.creation.instance.DefaultInstantiatorProvider"); DEFAULT_PLUGINS.put(AnnotationEngine.class.getName(), "org.mockito.internal.configuration.InjectingAnnotationEngine"); DEFAULT_PLUGINS.put(INLINE_ALIAS, "org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker"); } @Override public <T> T getDefaultPlugin(Class<T> pluginType) { - String className = DEFAULT_PLUGINS.get(pluginType.getName()); - return create(pluginType, className); + if (pluginType == InstantiatorProvider.class) { + //the implementation class is not configured via map so that we can reduce duplication + //(ensure that we are adapting the currently configured default implementation for InstantiatorProvider2) + String className = DEFAULT_PLUGINS.get(InstantiatorProvider2.class.getName()); + return pluginType.cast(new InstantiatorProvider2Adapter(create(InstantiatorProvider2.class, className))); + } else { + String className = DEFAULT_PLUGINS.get(pluginType.getName()); + return create(pluginType, className); + } } String getDefaultPluginClass(String classOrAlias) { diff --git a/src/main/java/org/mockito/internal/configuration/plugins/PluginInitializer.java b/src/main/java/org/mockito/internal/configuration/plugins/PluginInitializer.java new file mode 100644 index 0000000..0e28d91 --- /dev/null +++ b/src/main/java/org/mockito/internal/configuration/plugins/PluginInitializer.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.configuration.plugins; + +import org.mockito.internal.util.collections.Iterables; +import org.mockito.plugins.PluginSwitch; + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +class PluginInitializer { + + private final PluginSwitch pluginSwitch; + private final String alias; + private final DefaultMockitoPlugins plugins; + + PluginInitializer(PluginSwitch pluginSwitch, String alias, DefaultMockitoPlugins plugins) { + this.pluginSwitch = pluginSwitch; + this.alias = alias; + this.plugins = plugins; + } + + /** + * Equivalent to {@link java.util.ServiceLoader#load} but without requiring + * Java 6 / Android 2.3 (Gingerbread). + */ + public <T> T loadImpl(Class<T> service) { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + if (loader == null) { + loader = ClassLoader.getSystemClassLoader(); + } + Enumeration<URL> resources; + try { + resources = loader.getResources("mockito-extensions/" + service.getName()); + } catch (IOException e) { + throw new IllegalStateException("Failed to load " + service, e); + } + + try { + String classOrAlias = new PluginFinder(pluginSwitch).findPluginClass(Iterables.toIterable(resources)); + if (classOrAlias != null) { + if (classOrAlias.equals(alias)) { + classOrAlias = plugins.getDefaultPluginClass(alias); + } + Class<?> pluginClass = loader.loadClass(classOrAlias); + Object plugin = pluginClass.newInstance(); + return service.cast(plugin); + } + return null; + } catch (Exception e) { + throw new IllegalStateException( + "Failed to load " + service + " implementation declared in " + resources, e); + } + } +} diff --git a/src/main/java/org/mockito/internal/configuration/plugins/PluginLoader.java b/src/main/java/org/mockito/internal/configuration/plugins/PluginLoader.java index 0132dca..a230d0c 100644 --- a/src/main/java/org/mockito/internal/configuration/plugins/PluginLoader.java +++ b/src/main/java/org/mockito/internal/configuration/plugins/PluginLoader.java @@ -4,26 +4,24 @@ */ package org.mockito.internal.configuration.plugins; -import org.mockito.internal.util.collections.Iterables; import org.mockito.plugins.PluginSwitch; -import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import java.net.URL; -import java.util.Enumeration; class PluginLoader { - private final DefaultMockitoPlugins plugins = new DefaultMockitoPlugins(); + private final DefaultMockitoPlugins plugins; + private final PluginInitializer initializer; - private final PluginSwitch pluginSwitch; - - private String alias; + PluginLoader(DefaultMockitoPlugins plugins, PluginInitializer initializer) { + this.plugins = plugins; + this.initializer = initializer; + } - public PluginLoader(PluginSwitch pluginSwitch) { - this.pluginSwitch = pluginSwitch; + PluginLoader(PluginSwitch pluginSwitch) { + this(new DefaultMockitoPlugins(), new PluginInitializer(pluginSwitch, null, new DefaultMockitoPlugins())); } /** @@ -34,9 +32,8 @@ class PluginLoader { * the alias can be used as a convenience name for a known plugin. */ @Deprecated - PluginLoader withAlias(String name) { - alias = name; - return this; + PluginLoader(PluginSwitch pluginSwitch, String alias) { + this(new DefaultMockitoPlugins(), new PluginInitializer(pluginSwitch, alias, new DefaultMockitoPlugins())); } /** @@ -44,55 +41,39 @@ class PluginLoader { */ @SuppressWarnings("unchecked") <T> T loadPlugin(final Class<T> pluginType) { - try { - T plugin = loadImpl(pluginType); - if (plugin != null) { - return plugin; - } - - return plugins.getDefaultPlugin(pluginType); - } catch (final Throwable t) { - return (T) Proxy.newProxyInstance(pluginType.getClassLoader(), - new Class<?>[]{pluginType}, - new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - throw new IllegalStateException("Could not initialize plugin: " + pluginType, t); - } - }); - } + return (T) loadPlugin(pluginType, null); } /** - * Equivalent to {@link java.util.ServiceLoader#load} but without requiring - * Java 6 / Android 2.3 (Gingerbread). + * Scans the classpath for given {@code preferredPluginType}. If not found scan for {@code + * alternatePluginType}. If neither a preferred or alternate plugin is found, default to default + * class of {@code preferredPluginType}. + * + * @return An object of either {@code preferredPluginType} or {@code alternatePluginType} */ - private <T> T loadImpl(Class<T> service) { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - if (loader == null) { - loader = ClassLoader.getSystemClassLoader(); - } - Enumeration<URL> resources; - try { - resources = loader.getResources("mockito-extensions/" + service.getName()); - } catch (IOException e) { - throw new IllegalStateException("Failed to load " + service, e); - } - + @SuppressWarnings("unchecked") + <PreferredType, AlternateType> Object loadPlugin(final Class<PreferredType> preferredPluginType, final Class<AlternateType> alternatePluginType) { try { - String classOrAlias = new PluginFinder(pluginSwitch).findPluginClass(Iterables.toIterable(resources)); - if (classOrAlias != null) { - if (classOrAlias.equals(alias)) { - classOrAlias = plugins.getDefaultPluginClass(alias); + PreferredType preferredPlugin = initializer.loadImpl(preferredPluginType); + if (preferredPlugin != null) { + return preferredPlugin; + } else if (alternatePluginType != null) { + AlternateType alternatePlugin = initializer.loadImpl(alternatePluginType); + if (alternatePlugin != null) { + return alternatePlugin; } - Class<?> pluginClass = loader.loadClass(classOrAlias); - Object plugin = pluginClass.newInstance(); - return service.cast(plugin); } - return null; - } catch (Exception e) { - throw new IllegalStateException( - "Failed to load " + service + " implementation declared in " + resources, e); + + return plugins.getDefaultPlugin(preferredPluginType); + } catch (final Throwable t) { + return Proxy.newProxyInstance(preferredPluginType.getClassLoader(), + new Class<?>[]{preferredPluginType}, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + throw new IllegalStateException("Could not initialize plugin: " + preferredPluginType + " (alternate: " + alternatePluginType + ")", t); + } + }); } } } diff --git a/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java b/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java index d75242c..02e5d66 100644 --- a/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java +++ b/src/main/java/org/mockito/internal/configuration/plugins/PluginRegistry.java @@ -4,8 +4,10 @@ */ package org.mockito.internal.configuration.plugins; +import org.mockito.internal.creation.instance.InstantiatorProviderAdapter; import org.mockito.plugins.AnnotationEngine; import org.mockito.plugins.InstantiatorProvider; +import org.mockito.plugins.InstantiatorProvider2; import org.mockito.plugins.MockMaker; import org.mockito.plugins.PluginSwitch; import org.mockito.plugins.StackTraceCleanerProvider; @@ -15,19 +17,26 @@ class PluginRegistry { private final PluginSwitch pluginSwitch = new PluginLoader(new DefaultPluginSwitch()) .loadPlugin(PluginSwitch.class); - private final MockMaker mockMaker = new PluginLoader(pluginSwitch) - .withAlias(DefaultMockitoPlugins.INLINE_ALIAS) + private final MockMaker mockMaker = new PluginLoader(pluginSwitch, DefaultMockitoPlugins.INLINE_ALIAS) .loadPlugin(MockMaker.class); private final StackTraceCleanerProvider stackTraceCleanerProvider = new PluginLoader(pluginSwitch) .loadPlugin(StackTraceCleanerProvider.class); - private final InstantiatorProvider instantiatorProvider = new PluginLoader(pluginSwitch) - .loadPlugin(InstantiatorProvider.class); + private final InstantiatorProvider2 instantiatorProvider; private AnnotationEngine annotationEngine = new PluginLoader(pluginSwitch) .loadPlugin(AnnotationEngine.class); + PluginRegistry() { + Object impl = new PluginLoader(pluginSwitch).loadPlugin(InstantiatorProvider2.class, InstantiatorProvider.class); + if (impl instanceof InstantiatorProvider) { + instantiatorProvider = new InstantiatorProviderAdapter((InstantiatorProvider) impl); + } else { + instantiatorProvider = (InstantiatorProvider2) impl; + } + } + /** * The implementation of the stack trace cleaner */ @@ -50,10 +59,11 @@ class PluginRegistry { * Returns the instantiator provider available for the current runtime. * * <p>Returns {@link org.mockito.internal.creation.instance.DefaultInstantiatorProvider} if no - * {@link org.mockito.plugins.InstantiatorProvider} extension exists or is visible in the current classpath.</p> + * {@link org.mockito.plugins.InstantiatorProvider2} extension exists or is visible in the + * current classpath.</p> */ - InstantiatorProvider getInstantiatorProvider() { - return instantiatorProvider; + InstantiatorProvider2 getInstantiatorProvider() { + return instantiatorProvider; } /** diff --git a/src/main/java/org/mockito/internal/configuration/plugins/Plugins.java b/src/main/java/org/mockito/internal/configuration/plugins/Plugins.java index 2c214c2..f65fe89 100644 --- a/src/main/java/org/mockito/internal/configuration/plugins/Plugins.java +++ b/src/main/java/org/mockito/internal/configuration/plugins/Plugins.java @@ -5,7 +5,7 @@ package org.mockito.internal.configuration.plugins; import org.mockito.plugins.AnnotationEngine; -import org.mockito.plugins.InstantiatorProvider; +import org.mockito.plugins.InstantiatorProvider2; import org.mockito.plugins.MockMaker; import org.mockito.plugins.MockitoPlugins; import org.mockito.plugins.StackTraceCleanerProvider; @@ -38,9 +38,10 @@ public class Plugins { * Returns the instantiator provider available for the current runtime. * * <p>Returns {@link org.mockito.internal.creation.instance.DefaultInstantiatorProvider} if no - * {@link org.mockito.plugins.InstantiatorProvider} extension exists or is visible in the current classpath.</p> + * {@link org.mockito.plugins.InstantiatorProvider2} extension exists or is visible in the + * current classpath.</p> */ - public static InstantiatorProvider getInstantiatorProvider() { + public static InstantiatorProvider2 getInstantiatorProvider() { return registry.getInstantiatorProvider(); } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java index 50d7851..42f10ce 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java @@ -9,7 +9,7 @@ import org.mockito.Incubating; import org.mockito.exceptions.base.MockitoException; import org.mockito.exceptions.base.MockitoInitializationException; import org.mockito.internal.configuration.plugins.Plugins; -import org.mockito.internal.creation.instance.Instantiator; +import org.mockito.creation.instance.Instantiator; import org.mockito.internal.util.Platform; import org.mockito.internal.util.concurrent.WeakConcurrentMap; import org.mockito.invocation.MockHandler; @@ -189,7 +189,7 @@ public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker { ((MockAccess) instance).setMockitoInterceptor(mockMethodInterceptor); } return instance; - } catch (org.mockito.internal.creation.instance.InstantiationException e) { + } catch (org.mockito.creation.instance.InstantiationException e) { throw new MockitoException("Unable to create mock instance of type '" + type.getSimpleName() + "'", e); } } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java index 7d9f347..f39a1a2 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java @@ -16,6 +16,8 @@ import org.mockito.internal.debugging.LocationImpl; import org.mockito.internal.exceptions.stacktrace.ConditionalStackTraceFilter; import org.mockito.internal.invocation.RealMethod; import org.mockito.internal.invocation.SerializableMethod; +import org.mockito.internal.invocation.mockref.MockReference; +import org.mockito.internal.invocation.mockref.MockWeakReference; import org.mockito.internal.util.concurrent.WeakConcurrentMap; import java.io.IOException; @@ -107,18 +109,20 @@ public class MockMethodAdvice extends MockMethodDispatcher { return new ReturnValueWrapper(interceptor.doIntercept(instance, origin, arguments, - realMethod, + realMethod, new LocationImpl(t))); } @Override public boolean isMock(Object instance) { - return interceptors.containsKey(instance); + // We need to exclude 'interceptors.target' explicitly to avoid a recursive check on whether + // the map is a mock object what requires reading from the map. + return instance != interceptors.target && interceptors.containsKey(instance); } @Override public boolean isMocked(Object instance) { - return !selfCallInfo.isSelfInvocation(instance) && isMock(instance); + return selfCallInfo.checkSuperCall(instance) && isMock(instance); } @Override @@ -139,14 +143,14 @@ public class MockMethodAdvice extends MockMethodDispatcher { private final Method origin; - private final Object instance; + private final MockWeakReference<Object> instanceRef; private final Object[] arguments; private RealMethodCall(SelfCallInfo selfCallInfo, Method origin, Object instance, Object[] arguments) { this.selfCallInfo = selfCallInfo; this.origin = origin; - this.instance = instance; + this.instanceRef = new MockWeakReference<Object>(instance); this.arguments = arguments; } @@ -160,12 +164,8 @@ public class MockMethodAdvice extends MockMethodDispatcher { if (!Modifier.isPublic(origin.getDeclaringClass().getModifiers() & origin.getModifiers())) { origin.setAccessible(true); } - Object previous = selfCallInfo.replace(instance); - try { - return tryInvoke(origin, instance, arguments); - } finally { - selfCallInfo.set(previous); - } + selfCallInfo.set(instanceRef.get()); + return tryInvoke(origin, instanceRef.get(), arguments); } } @@ -176,14 +176,14 @@ public class MockMethodAdvice extends MockMethodDispatcher { private final SerializableMethod origin; - private final Object instance; + private final MockReference<Object> instanceRef; private final Object[] arguments; private SerializableRealMethodCall(String identifier, Method origin, Object instance, Object[] arguments) { this.origin = new SerializableMethod(origin); this.identifier = identifier; - this.instance = instance; + this.instanceRef = new MockWeakReference<Object>(instance); this.arguments = arguments; } @@ -198,13 +198,13 @@ public class MockMethodAdvice extends MockMethodDispatcher { if (!Modifier.isPublic(method.getDeclaringClass().getModifiers() & method.getModifiers())) { method.setAccessible(true); } - MockMethodDispatcher mockMethodDispatcher = MockMethodDispatcher.get(identifier, instance); + MockMethodDispatcher mockMethodDispatcher = MockMethodDispatcher.get(identifier, instanceRef.get()); if (!(mockMethodDispatcher instanceof MockMethodAdvice)) { throw new MockitoException("Unexpected dispatcher for advice-based super call"); } - Object previous = ((MockMethodAdvice) mockMethodDispatcher).selfCallInfo.replace(instance); + Object previous = ((MockMethodAdvice) mockMethodDispatcher).selfCallInfo.replace(instanceRef.get()); try { - return tryInvoke(method, instance, arguments); + return tryInvoke(method, instanceRef.get(), arguments); } finally { ((MockMethodAdvice) mockMethodDispatcher).selfCallInfo.set(previous); } @@ -252,14 +252,19 @@ public class MockMethodAdvice extends MockMethodDispatcher { private static class SelfCallInfo extends ThreadLocal<Object> { - Object replace(Object instance) { + Object replace(Object value) { Object current = get(); - set(instance); + set(value); return current; } - boolean isSelfInvocation(Object instance) { - return get() == instance; + boolean checkSuperCall(Object value) { + if (value == get()) { + set(null); + return false; + } else { + return true; + } } } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java index 7c2dec8..9066927 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodInterceptor.java @@ -13,12 +13,8 @@ import net.bytebuddy.implementation.bind.annotation.RuntimeType; import net.bytebuddy.implementation.bind.annotation.StubValue; import net.bytebuddy.implementation.bind.annotation.SuperCall; import net.bytebuddy.implementation.bind.annotation.This; -import org.mockito.internal.creation.DelegatingMethod; import org.mockito.internal.debugging.LocationImpl; -import org.mockito.internal.invocation.MockitoMethod; import org.mockito.internal.invocation.RealMethod; -import org.mockito.internal.invocation.SerializableMethod; -import org.mockito.internal.progress.SequenceNumber; import org.mockito.invocation.Location; import org.mockito.invocation.MockHandler; import org.mockito.mock.MockCreationSettings; @@ -28,6 +24,8 @@ import java.io.Serializable; import java.lang.reflect.Method; import java.util.concurrent.Callable; +import static org.mockito.internal.invocation.DefaultInvocationFactory.createInvocation; + public class MockMethodInterceptor implements Serializable { private static final long serialVersionUID = 7152947254057253027L; @@ -65,29 +63,6 @@ public class MockMethodInterceptor implements Serializable { return handler.handle(createInvocation(mock, invokedMethod, arguments, realMethod, mockCreationSettings, location)); } - public static InterceptedInvocation createInvocation(Object mock, Method invokedMethod, Object[] arguments, RealMethod realMethod, MockCreationSettings settings, Location location) { - return new InterceptedInvocation( - mock, - createMockitoMethod(invokedMethod, settings), - arguments, - realMethod, - location, - SequenceNumber.next() - ); - } - - public static InterceptedInvocation createInvocation(Object mock, Method invokedMethod, Object[] arguments, RealMethod realMethod, MockCreationSettings settings) { - return createInvocation(mock, invokedMethod, arguments, realMethod, settings, new LocationImpl()); - } - - private static MockitoMethod createMockitoMethod(Method method, MockCreationSettings settings) { - if (settings.isSerializable()) { - return new SerializableMethod(method); - } else { - return new DelegatingMethod(method); - } - } - public MockHandler getMockHandler() { return handler; } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java index e9a3ddf..79e89f2 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java @@ -6,7 +6,7 @@ package org.mockito.internal.creation.bytebuddy; import org.mockito.exceptions.base.MockitoException; import org.mockito.internal.configuration.plugins.Plugins; -import org.mockito.internal.creation.instance.Instantiator; +import org.mockito.creation.instance.Instantiator; import org.mockito.internal.util.Platform; import org.mockito.invocation.MockHandler; import org.mockito.mock.MockCreationSettings; @@ -60,7 +60,7 @@ public class SubclassByteBuddyMockMaker implements ClassCreatingMockMaker { "You might experience classloading issues, please ask the mockito mailing-list.", "" ), cce); - } catch (org.mockito.internal.creation.instance.InstantiationException e) { + } catch (org.mockito.creation.instance.InstantiationException e) { throw new MockitoException("Unable to create mock instance of type '" + mockedProxyType.getSuperclass().getSimpleName() + "'", e); } } diff --git a/src/main/java/org/mockito/internal/creation/instance/ConstructorInstantiator.java b/src/main/java/org/mockito/internal/creation/instance/ConstructorInstantiator.java index edf47c9..688526b 100644 --- a/src/main/java/org/mockito/internal/creation/instance/ConstructorInstantiator.java +++ b/src/main/java/org/mockito/internal/creation/instance/ConstructorInstantiator.java @@ -10,6 +10,8 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import org.mockito.creation.instance.Instantiator; +import org.mockito.creation.instance.InstantiationException; import org.mockito.internal.util.Primitives; import org.mockito.internal.util.reflection.AccessibilityChanger; diff --git a/src/main/java/org/mockito/internal/creation/instance/DefaultInstantiatorProvider.java b/src/main/java/org/mockito/internal/creation/instance/DefaultInstantiatorProvider.java index 22d06d1..9c414f3 100644 --- a/src/main/java/org/mockito/internal/creation/instance/DefaultInstantiatorProvider.java +++ b/src/main/java/org/mockito/internal/creation/instance/DefaultInstantiatorProvider.java @@ -4,10 +4,11 @@ */ package org.mockito.internal.creation.instance; +import org.mockito.creation.instance.Instantiator; import org.mockito.mock.MockCreationSettings; -import org.mockito.plugins.InstantiatorProvider; +import org.mockito.plugins.InstantiatorProvider2; -public class DefaultInstantiatorProvider implements InstantiatorProvider { +public class DefaultInstantiatorProvider implements InstantiatorProvider2 { private final static Instantiator INSTANCE = new ObjenesisInstantiator(); diff --git a/src/main/java/org/mockito/internal/creation/instance/InstantiationException.java b/src/main/java/org/mockito/internal/creation/instance/InstantiationException.java index c0151f1..86b5463 100644 --- a/src/main/java/org/mockito/internal/creation/instance/InstantiationException.java +++ b/src/main/java/org/mockito/internal/creation/instance/InstantiationException.java @@ -6,6 +6,14 @@ package org.mockito.internal.creation.instance; import org.mockito.exceptions.base.MockitoException; +/** + * @deprecated since 2.15.4 because this internal class was leaking from the public API. + * For information why deprecated, see {@link org.mockito.plugins.InstantiatorProvider2}. + * Use {@link org.mockito.creation.instance.Instantiator} and {@link org.mockito.creation.instance.InstantiationException} types instead. + * <p> + * Exception generated when {@link Instantiator#newInstance(Class)} failed. + */ +@Deprecated public class InstantiationException extends MockitoException { public InstantiationException(String message, Throwable cause) { diff --git a/src/main/java/org/mockito/internal/creation/instance/Instantiator.java b/src/main/java/org/mockito/internal/creation/instance/Instantiator.java index b1ee67d..85b6b3d 100644 --- a/src/main/java/org/mockito/internal/creation/instance/Instantiator.java +++ b/src/main/java/org/mockito/internal/creation/instance/Instantiator.java @@ -5,8 +5,13 @@ package org.mockito.internal.creation.instance; /** + * @deprecated since 2.15.4 because this internal class was leaking from the public API. + * For more information why deprecated, see {@link org.mockito.plugins.InstantiatorProvider2}. + * Use {@link org.mockito.creation.instance.Instantiator} instead. + * <p> * Provides instances of classes. */ +@Deprecated public interface Instantiator { /** diff --git a/src/main/java/org/mockito/internal/creation/instance/InstantiatorProvider2Adapter.java b/src/main/java/org/mockito/internal/creation/instance/InstantiatorProvider2Adapter.java new file mode 100644 index 0000000..a1ab5d5 --- /dev/null +++ b/src/main/java/org/mockito/internal/creation/instance/InstantiatorProvider2Adapter.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.creation.instance; + +import org.mockito.mock.MockCreationSettings; +import org.mockito.plugins.InstantiatorProvider; +import org.mockito.plugins.InstantiatorProvider2; + +/** + * Adapts new public API {@link InstantiatorProvider2} onto old, deprecated API {@link InstantiatorProvider} + */ +public class InstantiatorProvider2Adapter implements InstantiatorProvider { + private final InstantiatorProvider2 provider; + + public InstantiatorProvider2Adapter(InstantiatorProvider2 provider) { + this.provider = provider; + } + + @Override + public Instantiator getInstantiator(final MockCreationSettings<?> settings) { + return new Instantiator() { + @Override + public <T> T newInstance(Class<T> cls) throws InstantiationException { + try { + return provider.getInstantiator(settings).newInstance(cls); + } catch (org.mockito.creation.instance.InstantiationException e) { + throw new InstantiationException(e.getMessage(), e.getCause()); + } + } + }; + } +} diff --git a/src/main/java/org/mockito/internal/creation/instance/InstantiatorProviderAdapter.java b/src/main/java/org/mockito/internal/creation/instance/InstantiatorProviderAdapter.java new file mode 100644 index 0000000..c807925 --- /dev/null +++ b/src/main/java/org/mockito/internal/creation/instance/InstantiatorProviderAdapter.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.creation.instance; + +import org.mockito.creation.instance.InstantiationException; +import org.mockito.creation.instance.Instantiator; +import org.mockito.mock.MockCreationSettings; +import org.mockito.plugins.InstantiatorProvider; +import org.mockito.plugins.InstantiatorProvider2; + +/** + * Adapts old, deprecated {@link InstantiatorProvider} onto a new public {@link InstantiatorProvider2} API. + */ +public class InstantiatorProviderAdapter implements InstantiatorProvider2 { + private final InstantiatorProvider provider; + + public InstantiatorProviderAdapter(InstantiatorProvider provider) { + this.provider = provider; + } + + @Override + public Instantiator getInstantiator(final MockCreationSettings<?> settings) { + return new Instantiator() { + @Override + public <T> T newInstance(Class<T> cls) throws InstantiationException { + try { + return provider.getInstantiator(settings).newInstance(cls); + } catch (org.mockito.internal.creation.instance.InstantiationException e) { + throw new InstantiationException(e.getMessage(), e.getCause()); + } + } + }; + } +} diff --git a/src/main/java/org/mockito/internal/creation/instance/ObjenesisInstantiator.java b/src/main/java/org/mockito/internal/creation/instance/ObjenesisInstantiator.java index a0f0980..7e41f68 100644 --- a/src/main/java/org/mockito/internal/creation/instance/ObjenesisInstantiator.java +++ b/src/main/java/org/mockito/internal/creation/instance/ObjenesisInstantiator.java @@ -4,6 +4,7 @@ */ package org.mockito.internal.creation.instance; +import org.mockito.creation.instance.Instantiator; import org.mockito.internal.configuration.GlobalConfiguration; import org.objenesis.ObjenesisStd; diff --git a/src/main/java/org/mockito/internal/debugging/LocationImpl.java b/src/main/java/org/mockito/internal/debugging/LocationImpl.java index ce3de04..8561b62 100644 --- a/src/main/java/org/mockito/internal/debugging/LocationImpl.java +++ b/src/main/java/org/mockito/internal/debugging/LocationImpl.java @@ -11,11 +11,14 @@ import org.mockito.invocation.Location; public class LocationImpl implements Location, Serializable { private static final long serialVersionUID = -9054861157390980624L; + //Limit the amount of objects being created, as this class is heavily instantiated: + private static final StackTraceFilter defaultStackTraceFilter = new StackTraceFilter(); + private final Throwable stackTraceHolder; private final StackTraceFilter stackTraceFilter; public LocationImpl() { - this(new StackTraceFilter()); + this(defaultStackTraceFilter); } public LocationImpl(StackTraceFilter stackTraceFilter) { @@ -23,7 +26,7 @@ public class LocationImpl implements Location, Serializable { } public LocationImpl(Throwable stackTraceHolder) { - this(new StackTraceFilter(), stackTraceHolder); + this(defaultStackTraceFilter, stackTraceHolder); } private LocationImpl(StackTraceFilter stackTraceFilter, Throwable stackTraceHolder) { diff --git a/src/main/java/org/mockito/internal/exceptions/MockitoLimitations.java b/src/main/java/org/mockito/internal/exceptions/MockitoLimitations.java deleted file mode 100644 index c8edc79..0000000 --- a/src/main/java/org/mockito/internal/exceptions/MockitoLimitations.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2016 Mockito contributors - * This program is made available under the terms of the MIT License. - */ -package org.mockito.internal.exceptions; - -public class MockitoLimitations { - - public final static String NON_PUBLIC_PARENT = "Mocking methods declared on non-public parent classes is not supported."; - -} diff --git a/src/main/java/org/mockito/internal/exceptions/Reporter.java b/src/main/java/org/mockito/internal/exceptions/Reporter.java index cee97fd..57094c0 100644 --- a/src/main/java/org/mockito/internal/exceptions/Reporter.java +++ b/src/main/java/org/mockito/internal/exceptions/Reporter.java @@ -51,6 +51,8 @@ import static org.mockito.internal.util.StringUtil.join; */ public class Reporter { + private final static String NON_PUBLIC_PARENT = "Mocking methods declared on non-public parent classes is not supported."; + private Reporter() { } @@ -108,7 +110,7 @@ public class Reporter { "Also, this error might show up because:", "1. you stub either of: final/private/equals()/hashCode() methods.", " Those methods *cannot* be stubbed/verified.", - " " + MockitoLimitations.NON_PUBLIC_PARENT, + " " + NON_PUBLIC_PARENT, "2. inside when() you don't call method on mock but on some other object.", "" )); @@ -124,7 +126,7 @@ public class Reporter { "", "Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.", "Those methods *cannot* be stubbed/verified.", - MockitoLimitations.NON_PUBLIC_PARENT, + NON_PUBLIC_PARENT, "" )); } @@ -464,7 +466,7 @@ public class Reporter { "2. Somewhere in your test you are stubbing *final methods*. Sorry, Mockito does not verify/stub final methods.", "3. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies - ", " - with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.", - "4. " + MockitoLimitations.NON_PUBLIC_PARENT, + "4. " + NON_PUBLIC_PARENT, "" )); } @@ -531,7 +533,7 @@ public class Reporter { "", "Also, this error might show up because you use argument matchers with methods that cannot be mocked.", "Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().", - MockitoLimitations.NON_PUBLIC_PARENT, + NON_PUBLIC_PARENT, "" )); } @@ -857,7 +859,7 @@ public class Reporter { heading, "Clean & maintainable test code requires zero unnecessary code.", "Following stubbings are unnecessary (click to navigate to relevant line of code):" + stubbings, - "Please remove unnecessary stubbings or use 'silent' option. More info: javadoc for UnnecessaryStubbingException class." + "Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class." )); } @@ -888,7 +890,7 @@ public class Reporter { " - stubbing the same method multiple times using 'given().will()' or 'when().then()' API", " Please use 'will().given()' or 'doReturn().when()' API for stubbing.", " - stubbed method is intentionally invoked with different arguments by code under test", - " Please use 'default' or 'silent' JUnit Rule.", + " Please use default or 'silent' JUnit Rule (equivalent of Strictness.LENIENT).", "For more information see javadoc for PotentialStubbingProblem class.")); } diff --git a/src/main/java/org/mockito/internal/framework/DefaultMockitoSession.java b/src/main/java/org/mockito/internal/framework/DefaultMockitoSession.java index 40b24bb..c900bf7 100644 --- a/src/main/java/org/mockito/internal/framework/DefaultMockitoSession.java +++ b/src/main/java/org/mockito/internal/framework/DefaultMockitoSession.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Mockito contributors + * Copyright (c) 2018 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.framework; @@ -14,13 +14,17 @@ import org.mockito.internal.junit.UniversalTestListener; import org.mockito.internal.util.MockitoLogger; import org.mockito.quality.Strictness; +import java.util.List; + public class DefaultMockitoSession implements MockitoSession { - private final Object testClassInstance; + private final List<Object> testClassInstances; + private final String name; private final UniversalTestListener listener; - public DefaultMockitoSession(Object testClassInstance, Strictness strictness, MockitoLogger logger) { - this.testClassInstance = testClassInstance; + public DefaultMockitoSession(List<Object> testClassInstances, String name, Strictness strictness, MockitoLogger logger) { + this.testClassInstances = testClassInstances; + this.name = name; listener = new UniversalTestListener(strictness, logger); try { //So that the listener can capture mock creation events @@ -28,10 +32,23 @@ public class DefaultMockitoSession implements MockitoSession { } catch (RedundantListenerException e) { Reporter.unfinishedMockingSession(); } - MockitoAnnotations.initMocks(testClassInstance); + for (Object testClassInstance : testClassInstances) { + MockitoAnnotations.initMocks(testClassInstance); + } + } + + @Override + public void setStrictness(Strictness strictness) { + listener.setStrictness(strictness); } + @Override public void finishMocking() { + finishMocking(null); + } + + @Override + public void finishMocking(final Throwable failure) { //Cleaning up the state, we no longer need the listener hooked up //The listener implements MockCreationListener and at this point //we no longer need to listen on mock creation events. We are wrapping up the session @@ -39,18 +56,20 @@ public class DefaultMockitoSession implements MockitoSession { //Emit test finished event so that validation such as strict stubbing can take place listener.testFinished(new TestFinishedEvent() { + @Override public Throwable getFailure() { - return null; + return failure; } - public Object getTestClassInstance() { - return testClassInstance; - } - public String getTestMethodName() { - return null; + @Override + public String getTestName() { + return name; } }); - //Finally, validate user's misuse of Mockito framework. - Mockito.validateMockitoUsage(); + //Validate only when there is no test failure to avoid reporting multiple problems + if (failure == null) { + //Finally, validate user's misuse of Mockito framework. + Mockito.validateMockitoUsage(); + } } } diff --git a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java index 38f18e9..57a9d43 100644 --- a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java +++ b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java @@ -8,7 +8,6 @@ import org.mockito.internal.creation.settings.CreationSettings; import org.mockito.internal.invocation.InvocationMatcher; import org.mockito.internal.invocation.MatchersBinder; import org.mockito.internal.listeners.StubbingLookupListener; -import org.mockito.invocation.InvocationContainer; import org.mockito.internal.stubbing.InvocationContainerImpl; import org.mockito.internal.stubbing.OngoingStubbingImpl; import org.mockito.internal.stubbing.StubbedInvocationMatcher; @@ -16,6 +15,7 @@ import org.mockito.internal.stubbing.answers.DefaultAnswerValidator; import org.mockito.internal.verification.MockAwareVerificationMode; import org.mockito.internal.verification.VerificationDataImpl; import org.mockito.invocation.Invocation; +import org.mockito.invocation.InvocationContainer; import org.mockito.invocation.MockHandler; import org.mockito.mock.MockCreationSettings; import org.mockito.verification.VerificationMode; @@ -92,7 +92,14 @@ public class MockHandlerImpl<T> implements MockHandler<T> { if (stubbing != null) { stubbing.captureArgumentsFrom(invocation); - return stubbing.answer(invocation); + + try { + return stubbing.answer(invocation); + } finally { + //Needed so that we correctly isolate stubbings in some scenarios + //see MockitoStubbedCallInAnswerTest or issue #1279 + mockingProgress().reportOngoingStubbing(ongoingStubbing); + } } else { Object ret = mockSettings.getDefaultAnswer().answer(invocation); DefaultAnswerValidator.validateReturnValueFor(invocation, ret); diff --git a/src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java b/src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java index af8fd3d..d08f6b1 100644 --- a/src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java +++ b/src/main/java/org/mockito/internal/invocation/DefaultInvocationFactory.java @@ -4,9 +4,13 @@ */ package org.mockito.internal.invocation; -import org.mockito.internal.creation.bytebuddy.MockMethodInterceptor; +import org.mockito.internal.creation.DelegatingMethod; +import org.mockito.internal.invocation.mockref.MockWeakReference; +import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.progress.SequenceNumber; import org.mockito.invocation.Invocation; import org.mockito.invocation.InvocationFactory; +import org.mockito.invocation.Location; import org.mockito.mock.MockCreationSettings; import java.lang.reflect.Method; @@ -14,8 +18,41 @@ import java.util.concurrent.Callable; public class DefaultInvocationFactory implements InvocationFactory { - public Invocation createInvocation(Object target, MockCreationSettings settings, Method method, Callable realMethod, Object... args) { - RealMethod.FromCallable superMethod = new RealMethod.FromCallable(realMethod); - return MockMethodInterceptor.createInvocation(target, method, args, superMethod, settings); + public Invocation createInvocation(Object target, MockCreationSettings settings, Method method, final Callable realMethod, Object... args) { + RealMethod superMethod = new RealMethod.FromCallable(realMethod); + return createInvocation(target, settings, method, superMethod, args); + } + + public Invocation createInvocation(Object target, MockCreationSettings settings, Method method, RealMethodBehavior realMethod, Object... args) { + RealMethod superMethod = new RealMethod.FromBehavior(realMethod); + return createInvocation(target, settings, method, superMethod, args); + } + + private Invocation createInvocation(Object target, MockCreationSettings settings, Method method, RealMethod superMethod, Object[] args) { + return createInvocation(target, method, args, superMethod, settings); + } + + public static InterceptedInvocation createInvocation(Object mock, Method invokedMethod, Object[] arguments, RealMethod realMethod, MockCreationSettings settings, Location location) { + return new InterceptedInvocation( + new MockWeakReference<Object>(mock), + createMockitoMethod(invokedMethod, settings), + arguments, + realMethod, + location, + SequenceNumber.next() + ); + } + + private static InterceptedInvocation createInvocation(Object mock, Method invokedMethod, Object[] + arguments, RealMethod realMethod, MockCreationSettings settings) { + return createInvocation(mock, invokedMethod, arguments, realMethod, settings, new LocationImpl()); + } + + private static MockitoMethod createMockitoMethod(Method method, MockCreationSettings settings) { + if (settings.isSerializable()) { + return new SerializableMethod(method); + } else { + return new DelegatingMethod(method); + } } } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InterceptedInvocation.java b/src/main/java/org/mockito/internal/invocation/InterceptedInvocation.java index 9c7b49e..b9cf072 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InterceptedInvocation.java +++ b/src/main/java/org/mockito/internal/invocation/InterceptedInvocation.java @@ -2,12 +2,10 @@ * Copyright (c) 2016 Mockito contributors * This program is made available under the terms of the MIT License. */ -package org.mockito.internal.creation.bytebuddy; +package org.mockito.internal.invocation; +import org.mockito.internal.invocation.mockref.MockReference; import org.mockito.internal.exceptions.VerificationAwareInvocation; -import org.mockito.internal.invocation.ArgumentsProcessor; -import org.mockito.internal.invocation.MockitoMethod; -import org.mockito.internal.invocation.RealMethod; import org.mockito.internal.reporting.PrintSettings; import org.mockito.invocation.Invocation; import org.mockito.invocation.Location; @@ -22,7 +20,7 @@ public class InterceptedInvocation implements Invocation, VerificationAwareInvoc private static final long serialVersionUID = 475027563923510472L; - private final Object mock; + private final MockReference<Object> mockRef; private final MockitoMethod mockitoMethod; private final Object[] arguments, rawArguments; private final RealMethod realMethod; @@ -35,13 +33,13 @@ public class InterceptedInvocation implements Invocation, VerificationAwareInvoc private boolean isIgnoredForVerification; private StubInfo stubInfo; - public InterceptedInvocation(Object mock, + public InterceptedInvocation(MockReference<Object> mockRef, MockitoMethod mockitoMethod, Object[] arguments, RealMethod realMethod, Location location, int sequenceNumber) { - this.mock = mock; + this.mockRef = mockRef; this.mockitoMethod = mockitoMethod; this.arguments = ArgumentsProcessor.expandArgs(mockitoMethod, arguments); this.rawArguments = arguments; @@ -102,7 +100,7 @@ public class InterceptedInvocation implements Invocation, VerificationAwareInvoc @Override public Object getMock() { - return mock; + return mockRef.get(); } @Override @@ -141,7 +139,7 @@ public class InterceptedInvocation implements Invocation, VerificationAwareInvoc return false; } InterceptedInvocation other = (InterceptedInvocation) o; - return this.mock.equals(other.mock) + return this.mockRef.get().equals(other.mockRef.get()) && this.mockitoMethod.equals(other.mockitoMethod) && this.equalArguments(other.arguments); } diff --git a/src/main/java/org/mockito/internal/invocation/RealMethod.java b/src/main/java/org/mockito/internal/invocation/RealMethod.java index cd74d3c..b7c8c17 100644 --- a/src/main/java/org/mockito/internal/invocation/RealMethod.java +++ b/src/main/java/org/mockito/internal/invocation/RealMethod.java @@ -5,6 +5,7 @@ package org.mockito.internal.invocation; import org.mockito.internal.exceptions.stacktrace.ConditionalStackTraceFilter; +import org.mockito.invocation.InvocationFactory; import org.mockito.invocation.InvocationOnMock; import java.io.Serializable; @@ -31,14 +32,23 @@ public interface RealMethod extends Serializable { } } - class FromCallable implements RealMethod { + class FromCallable extends FromBehavior implements RealMethod { + public FromCallable(final Callable<?> callable) { + super(new InvocationFactory.RealMethodBehavior() { + @Override + public Object call() throws Throwable { + return callable.call(); + } + }); + } + } - private static final long serialVersionUID = 47957363950483625L; + class FromBehavior implements RealMethod { - private final Callable<?> callable; + private final InvocationFactory.RealMethodBehavior<?> behavior; - public FromCallable(Callable<?> callable) { - this.callable = callable; + FromBehavior(InvocationFactory.RealMethodBehavior<?> behavior) { + this.behavior = behavior; } @Override @@ -49,7 +59,7 @@ public interface RealMethod extends Serializable { @Override public Object invoke() throws Throwable { try { - return callable.call(); + return behavior.call(); } catch (Throwable t) { new ConditionalStackTraceFilter().filter(t); throw t; diff --git a/src/main/java/org/mockito/internal/invocation/mockref/MockReference.java b/src/main/java/org/mockito/internal/invocation/mockref/MockReference.java new file mode 100644 index 0000000..0dac2ea --- /dev/null +++ b/src/main/java/org/mockito/internal/invocation/mockref/MockReference.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2018 Mockito contributors + * This program is made available under the terms of the MIT License. + */ + +package org.mockito.internal.invocation.mockref; + +import java.io.Serializable; + +/** + * To avoid memory leaks for certain implementations of MockMaker, + * we need to use weak mock references internally. See #1313 + */ +public interface MockReference<T> extends Serializable { + T get(); +} diff --git a/src/main/java/org/mockito/internal/invocation/mockref/MockStrongReference.java b/src/main/java/org/mockito/internal/invocation/mockref/MockStrongReference.java new file mode 100644 index 0000000..3b20ee5 --- /dev/null +++ b/src/main/java/org/mockito/internal/invocation/mockref/MockStrongReference.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Mockito contributors + * This program is made available under the terms of the MIT License. + */ + +package org.mockito.internal.invocation.mockref; + +import java.io.ObjectStreamException; + +public class MockStrongReference<T> implements MockReference<T> { + + private final T ref; + private final boolean deserializeAsWeakRef; + + public MockStrongReference(T ref, boolean deserializeAsWeakRef) { + this.ref = ref; + this.deserializeAsWeakRef = deserializeAsWeakRef; + } + + @Override + public T get() { + return ref; + } + + private Object readResolve() throws ObjectStreamException { + if (deserializeAsWeakRef) { + return new MockWeakReference<T>(ref); + } else { + return this; + } + } +} diff --git a/src/main/java/org/mockito/internal/invocation/mockref/MockWeakReference.java b/src/main/java/org/mockito/internal/invocation/mockref/MockWeakReference.java new file mode 100644 index 0000000..256745b --- /dev/null +++ b/src/main/java/org/mockito/internal/invocation/mockref/MockWeakReference.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Mockito contributors + * This program is made available under the terms of the MIT License. + */ + +package org.mockito.internal.invocation.mockref; + +import java.io.ObjectStreamException; +import java.lang.ref.WeakReference; + +/** + * A weak reference that is converted into a strong reference when serialized. + * See {@link MockReference}. + */ +public class MockWeakReference<T> implements MockReference<T> { + + private final WeakReference<T> ref; + + public MockWeakReference(T t) { + this.ref = new WeakReference<T>(t); + } + + private Object writeReplace() throws ObjectStreamException { + return new MockStrongReference<T>(get(), true); + } + + @Override + public T get() { + T ref = this.ref.get(); + + if (ref == null) { + throw new IllegalStateException("The mock object was garbage collected. " + + "This should not happen in normal circumstances when using public API. " + + "Typically, the test class keeps strong reference to the mock object " + + "and it prevents getting the mock collected. Mockito internally needs " + + "to keep weak references to mock objects to avoid memory leaks for " + + "certain types of MockMaker implementations. If you see this exception " + + "using Mockito public API, please file a bug. For more information see " + + "issue #1313."); + } + + return ref; + } +} diff --git a/src/main/java/org/mockito/internal/junit/DefaultTestFinishedEvent.java b/src/main/java/org/mockito/internal/junit/DefaultTestFinishedEvent.java index 172868c..c3b348b 100644 --- a/src/main/java/org/mockito/internal/junit/DefaultTestFinishedEvent.java +++ b/src/main/java/org/mockito/internal/junit/DefaultTestFinishedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Mockito contributors + * Copyright (c) 2018 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.junit; @@ -15,15 +15,13 @@ public class DefaultTestFinishedEvent implements TestFinishedEvent { this.testFailure = testFailure; } + @Override public Throwable getFailure() { return testFailure; } - public Object getTestClassInstance() { - return testClassInstance; - } - - public String getTestMethodName() { - return testMethodName; + @Override + public String getTestName() { + return testClassInstance.getClass().getSimpleName() + "." + testMethodName; } } diff --git a/src/main/java/org/mockito/internal/junit/JUnitRule.java b/src/main/java/org/mockito/internal/junit/JUnitRule.java index 8763354..b825416 100644 --- a/src/main/java/org/mockito/internal/junit/JUnitRule.java +++ b/src/main/java/org/mockito/internal/junit/JUnitRule.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Mockito contributors + * Copyright (c) 2018 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.junit; @@ -7,9 +7,10 @@ package org.mockito.internal.junit; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.Statement; import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.mockito.quality.Strictness; +import org.mockito.MockitoSession; +import org.mockito.internal.session.MockitoSessionLoggerAdapter; import org.mockito.internal.util.MockitoLogger; +import org.mockito.quality.Strictness; import org.mockito.junit.MockitoRule; /** @@ -18,44 +19,32 @@ import org.mockito.junit.MockitoRule; public class JUnitRule implements MockitoRule { private final MockitoLogger logger; - private final UniversalTestListener listener; + private Strictness strictness; + private MockitoSession session; /** - * @param logger target for the stubbing warnings * @param strictness how strict mocking / stubbing is concerned */ public JUnitRule(MockitoLogger logger, Strictness strictness) { this.logger = logger; - this.listener = new UniversalTestListener(strictness, logger); + this.strictness = strictness; } @Override public Statement apply(final Statement base, final FrameworkMethod method, final Object target) { return new Statement() { public void evaluate() throws Throwable { - //Ideally, JUnit rule should use MockitoSession API so that it dogfoods our public API. - //See https://github.com/mockito/mockito/issues/898 - Mockito.framework().addListener(listener); - Throwable testFailure; - try { - //mock initialization could be part of listeners but to avoid duplication I left it here: - MockitoAnnotations.initMocks(target); - testFailure = evaluateSafely(base); - } finally { - Mockito.framework().removeListener(listener); - } - - //If the 'testFinished' fails below, we don't see the original failure, thrown later - DefaultTestFinishedEvent event = new DefaultTestFinishedEvent(target, method.getName(), testFailure); - listener.testFinished(event); - + session = Mockito.mockitoSession() + .name(target.getClass().getSimpleName() + "." + method.getName()) + .strictness(strictness) + .logger(new MockitoSessionLoggerAdapter(logger)) + .initMocks(target) + .startMocking(); + Throwable testFailure = evaluateSafely(base); + session.finishMocking(testFailure); if (testFailure != null) { throw testFailure; } - - //Validate only when there is no test failure to avoid reporting multiple problems - //This could be part of the listener but to avoid duplication I left it here: - Mockito.validateMockitoUsage(); } private Throwable evaluateSafely(Statement base) { @@ -70,11 +59,16 @@ public class JUnitRule implements MockitoRule { } public MockitoRule silent() { - return new JUnitRule(logger, Strictness.LENIENT); + return strictness(Strictness.LENIENT); } public MockitoRule strictness(Strictness strictness) { - this.listener.setStrictness(strictness); + this.strictness = strictness; + // session is null when this method is called during initialization of + // the @Rule field of the test class + if (session != null) { + session.setStrictness(strictness); + } return this; } } diff --git a/src/main/java/org/mockito/internal/junit/MismatchReportingTestListener.java b/src/main/java/org/mockito/internal/junit/MismatchReportingTestListener.java index 408c581..26d4e4e 100644 --- a/src/main/java/org/mockito/internal/junit/MismatchReportingTestListener.java +++ b/src/main/java/org/mockito/internal/junit/MismatchReportingTestListener.java @@ -1,10 +1,9 @@ /* - * Copyright (c) 2017 Mockito contributors + * Copyright (c) 2018 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.junit; -import org.mockito.internal.junit.util.TestName; import org.mockito.internal.util.MockitoLogger; import org.mockito.mock.MockCreationSettings; @@ -30,10 +29,9 @@ public class MismatchReportingTestListener implements MockitoTestListener { //TODO make it better, it's easy to forget to clean up mocks and we still create new instance of list that nobody will read, it's also duplicated mocks = new LinkedList<Object>(); - String testName = TestName.getTestName(event); if (event.getFailure() != null) { //print unused stubbings only when test succeeds to avoid reporting multiple problems and confusing users - new ArgMismatchFinder().getStubbingArgMismatches(createdMocks).format(testName, logger); + new ArgMismatchFinder().getStubbingArgMismatches(createdMocks).format(event.getTestName(), logger); } } diff --git a/src/main/java/org/mockito/internal/junit/TestFinishedEvent.java b/src/main/java/org/mockito/internal/junit/TestFinishedEvent.java index 18b099a..43b373a 100644 --- a/src/main/java/org/mockito/internal/junit/TestFinishedEvent.java +++ b/src/main/java/org/mockito/internal/junit/TestFinishedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Mockito contributors + * Copyright (c) 2018 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.junit; @@ -8,8 +8,6 @@ public interface TestFinishedEvent { Throwable getFailure(); - Object getTestClassInstance(); - - String getTestMethodName(); + String getTestName(); } diff --git a/src/main/java/org/mockito/internal/junit/UniversalTestListener.java b/src/main/java/org/mockito/internal/junit/UniversalTestListener.java index 8db020b..66c9dee 100644 --- a/src/main/java/org/mockito/internal/junit/UniversalTestListener.java +++ b/src/main/java/org/mockito/internal/junit/UniversalTestListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Mockito contributors + * Copyright (c) 2018 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.junit; @@ -60,14 +60,13 @@ public class UniversalTestListener implements MockitoTestListener { } private static void emitWarnings(MockitoLogger logger, TestFinishedEvent event, Collection<Object> mocks) { - String testName = event.getTestClassInstance().getClass().getSimpleName() + "." + event.getTestMethodName(); if (event.getFailure() != null) { //print stubbing mismatches only when there is a test failure //to avoid false negatives. Give hint only when test fails. - new ArgMismatchFinder().getStubbingArgMismatches(mocks).format(testName, logger); + new ArgMismatchFinder().getStubbingArgMismatches(mocks).format(event.getTestName(), logger); } else { //print unused stubbings only when test succeeds to avoid reporting multiple problems and confusing users - new UnusedStubbingsFinder().getUnusedStubbings(mocks).format(testName, logger); + new UnusedStubbingsFinder().getUnusedStubbings(mocks).format(event.getTestName(), logger); } } diff --git a/src/main/java/org/mockito/internal/junit/util/TestName.java b/src/main/java/org/mockito/internal/junit/util/TestName.java deleted file mode 100644 index 6f09937..0000000 --- a/src/main/java/org/mockito/internal/junit/util/TestName.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2017 Mockito contributors - * This program is made available under the terms of the MIT License. - */ -package org.mockito.internal.junit.util; - -import org.mockito.internal.junit.TestFinishedEvent; - -/** - * Provides test name - */ -public class TestName { - - public static String getTestName(TestFinishedEvent event) { - return event.getTestClassInstance().getClass().getSimpleName() + "." + event.getTestMethodName(); - } -} diff --git a/src/main/java/org/mockito/internal/matchers/text/ArrayIterator.java b/src/main/java/org/mockito/internal/matchers/text/ArrayIterator.java deleted file mode 100644 index 32d3c66..0000000 --- a/src/main/java/org/mockito/internal/matchers/text/ArrayIterator.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2016 Mockito contributors - * This program is made available under the terms of the MIT License. - */ -package org.mockito.internal.matchers.text; - -import java.lang.reflect.Array; -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * Inspired on hamcrest, internal package class, - * TODO add specific unit tests instead of relying on higher level unit tests - */ -class ArrayIterator implements Iterator<Object> { - - private final Object array; - private int currentIndex = 0; - - public ArrayIterator(Object array) { - if (array == null) { - //TODO extract a small utility for null-checking - throw new IllegalArgumentException("Expected array instance but got null"); - } - if (!array.getClass().isArray()) { - throw new IllegalArgumentException("Expected array but got object of type: " - + array.getClass() + ", the object: " + array.toString()); - } - this.array = array; - } - - public boolean hasNext() { - return currentIndex < Array.getLength(array); - } - - public Object next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return Array.get(array, currentIndex++); - } - - public void remove() { - throw new UnsupportedOperationException("cannot remove items from an array"); - } -} diff --git a/src/main/java/org/mockito/internal/matchers/text/ValuePrinter.java b/src/main/java/org/mockito/internal/matchers/text/ValuePrinter.java index f741cbc..6f5882f 100644 --- a/src/main/java/org/mockito/internal/matchers/text/ValuePrinter.java +++ b/src/main/java/org/mockito/internal/matchers/text/ValuePrinter.java @@ -4,6 +4,7 @@ */ package org.mockito.internal.matchers.text; +import java.lang.reflect.Array; import java.util.Iterator; import java.util.Map; @@ -21,7 +22,7 @@ public class ValuePrinter { * Prints given value so that it is neatly readable by humans. * Handles explosive toString() implementations. */ - public static String print(Object value) { + public static String print(final Object value) { if (value == null) { return "null"; } @@ -50,7 +51,21 @@ public class ValuePrinter { return printMap((Map<?, ?>) value); } if (value.getClass().isArray()) { - return printValues("[", ", ", "]", new ArrayIterator(value)); + return printValues("[", ", ", "]", new Iterator<Object>() { + private int currentIndex = 0; + + public boolean hasNext() { + return currentIndex < Array.getLength(value); + } + + public Object next() { + return Array.get(value, currentIndex++); + } + + public void remove() { + throw new UnsupportedOperationException("cannot remove items from an array"); + } + }); } if (value instanceof FormattedText) { return (((FormattedText) value).getText()); diff --git a/src/main/java/org/mockito/internal/progress/MockingProgressImpl.java b/src/main/java/org/mockito/internal/progress/MockingProgressImpl.java index a0f8c66..2685ddf 100644 --- a/src/main/java/org/mockito/internal/progress/MockingProgressImpl.java +++ b/src/main/java/org/mockito/internal/progress/MockingProgressImpl.java @@ -47,8 +47,8 @@ public class MockingProgressImpl implements MockingProgress { }; } - public void reportOngoingStubbing(OngoingStubbing iOngoingStubbing) { - this.ongoingStubbing = iOngoingStubbing; + public void reportOngoingStubbing(OngoingStubbing ongoingStubbing) { + this.ongoingStubbing = ongoingStubbing; } public OngoingStubbing<?> pullOngoingStubbing() { @@ -129,7 +129,7 @@ public class MockingProgressImpl implements MockingProgress { } public String toString() { - return "iOngoingStubbing: " + ongoingStubbing + + return "ongoingStubbing: " + ongoingStubbing + ", verificationMode: " + verificationMode + ", stubbingInProgress: " + stubbingInProgress; } diff --git a/src/main/java/org/mockito/internal/runners/DefaultInternalRunner.java b/src/main/java/org/mockito/internal/runners/DefaultInternalRunner.java index 897e613..2f26ff1 100644 --- a/src/main/java/org/mockito/internal/runners/DefaultInternalRunner.java +++ b/src/main/java/org/mockito/internal/runners/DefaultInternalRunner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 Mockito contributors + * Copyright (c) 2018 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.runners; @@ -16,6 +16,7 @@ import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.mockito.internal.junit.DefaultTestFinishedEvent; import org.mockito.internal.junit.MockitoTestListener; import org.mockito.internal.util.Supplier; diff --git a/src/main/java/org/mockito/internal/runners/DefaultTestFinishedEvent.java b/src/main/java/org/mockito/internal/runners/DefaultTestFinishedEvent.java deleted file mode 100644 index 2eee23f..0000000 --- a/src/main/java/org/mockito/internal/runners/DefaultTestFinishedEvent.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2017 Mockito contributors - * This program is made available under the terms of the MIT License. - */ -package org.mockito.internal.runners; - -import org.mockito.internal.junit.TestFinishedEvent; - -class DefaultTestFinishedEvent implements TestFinishedEvent { - - private final Object testClassInstance; - private final String testMethodName; - private final Throwable failure; - - DefaultTestFinishedEvent(Object testClassInstance, String testMethodName, Throwable failure) { - this.testClassInstance = testClassInstance; - this.testMethodName = testMethodName; - this.failure = failure; - } - - @Override - public Object getTestClassInstance() { - return testClassInstance; - } - - @Override - public String getTestMethodName() { - return testMethodName; - } - - @Override - public Throwable getFailure() { - return failure; - } -} diff --git a/src/main/java/org/mockito/internal/session/DefaultMockitoSessionBuilder.java b/src/main/java/org/mockito/internal/session/DefaultMockitoSessionBuilder.java index bfd6be8..d9b21e5 100644 --- a/src/main/java/org/mockito/internal/session/DefaultMockitoSessionBuilder.java +++ b/src/main/java/org/mockito/internal/session/DefaultMockitoSessionBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Mockito contributors + * Copyright (c) 2018 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.session; @@ -7,17 +7,44 @@ package org.mockito.internal.session; import org.mockito.MockitoSession; import org.mockito.internal.framework.DefaultMockitoSession; import org.mockito.internal.util.ConsoleMockitoLogger; +import org.mockito.internal.util.MockitoLogger; import org.mockito.quality.Strictness; import org.mockito.session.MockitoSessionBuilder; +import org.mockito.session.MockitoSessionLogger; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.Collections.emptyList; public class DefaultMockitoSessionBuilder implements MockitoSessionBuilder { - private Object testClassInstance; + private List<Object> testClassInstances = new ArrayList<Object>(); + private String name; private Strictness strictness; + private MockitoSessionLogger logger; @Override public MockitoSessionBuilder initMocks(Object testClassInstance) { - this.testClassInstance = testClassInstance; + if (testClassInstance != null) { + this.testClassInstances.add(testClassInstance); + } + return this; + } + + @Override + public MockitoSessionBuilder initMocks(Object... testClassInstances) { + if (testClassInstances != null) { + for (Object instance : testClassInstances) { + initMocks(instance); + } + } + return this; + } + + @Override + public MockitoSessionBuilder name(String name) { + this.name = name; return this; } @@ -28,10 +55,26 @@ public class DefaultMockitoSessionBuilder implements MockitoSessionBuilder { } @Override + public MockitoSessionBuilder logger(MockitoSessionLogger logger) { + this.logger = logger; + return this; + } + + @Override public MockitoSession startMocking() { //Configure default values - Object effectiveTest = this.testClassInstance == null ? new Object() : this.testClassInstance; + List<Object> effectiveTestClassInstances; + String effectiveName; + if (testClassInstances.isEmpty()) { + effectiveTestClassInstances = emptyList(); + effectiveName = this.name == null ? "<Unnamed Session>" : this.name; + } else { + effectiveTestClassInstances = new ArrayList<Object>(testClassInstances); + Object lastTestClassInstance = testClassInstances.get(testClassInstances.size() - 1); + effectiveName = this.name == null ? lastTestClassInstance.getClass().getName() : this.name; + } Strictness effectiveStrictness = this.strictness == null ? Strictness.STRICT_STUBS : this.strictness; - return new DefaultMockitoSession(effectiveTest, effectiveStrictness, new ConsoleMockitoLogger()); + MockitoLogger logger = this.logger == null ? new ConsoleMockitoLogger() : new MockitoLoggerAdapter(this.logger); + return new DefaultMockitoSession(effectiveTestClassInstances, effectiveName, effectiveStrictness, logger); } } diff --git a/src/main/java/org/mockito/internal/session/MockitoLoggerAdapter.java b/src/main/java/org/mockito/internal/session/MockitoLoggerAdapter.java new file mode 100644 index 0000000..b7329e7 --- /dev/null +++ b/src/main/java/org/mockito/internal/session/MockitoLoggerAdapter.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.session; + +import org.mockito.internal.util.MockitoLogger; +import org.mockito.session.MockitoSessionLogger; + +class MockitoLoggerAdapter implements MockitoLogger { + + private final MockitoSessionLogger logger; + + MockitoLoggerAdapter(MockitoSessionLogger logger) { + this.logger = logger; + } + + @Override + public void log(Object what) { + logger.log(String.valueOf(what)); + } +} diff --git a/src/main/java/org/mockito/internal/session/MockitoSessionLoggerAdapter.java b/src/main/java/org/mockito/internal/session/MockitoSessionLoggerAdapter.java new file mode 100644 index 0000000..2e8634b --- /dev/null +++ b/src/main/java/org/mockito/internal/session/MockitoSessionLoggerAdapter.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.session; + +import org.mockito.internal.util.MockitoLogger; +import org.mockito.session.MockitoSessionLogger; + +public class MockitoSessionLoggerAdapter implements MockitoSessionLogger { + + private final MockitoLogger logger; + + public MockitoSessionLoggerAdapter(MockitoLogger logger) { + this.logger = logger; + } + + @Override + public void log(String hint) { + logger.log(hint); + } +} diff --git a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java index f88fc03..120320b 100644 --- a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java @@ -4,11 +4,6 @@ */ package org.mockito.internal.stubbing; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - import org.mockito.internal.invocation.StubInfoImpl; import org.mockito.internal.verification.DefaultRegisteredInvocations; import org.mockito.internal.verification.RegisteredInvocations; @@ -21,6 +16,11 @@ import org.mockito.stubbing.Answer; import org.mockito.stubbing.Stubbing; import org.mockito.stubbing.ValidableAnswer; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress; @SuppressWarnings("unchecked") @@ -93,10 +93,6 @@ public class InvocationContainerImpl implements InvocationContainer, Serializabl return null; } - public void addAnswerForVoidMethod(Answer answer) { - answersForStubbing.add(answer); - } - public void setAnswersForStubbing(List<Answer<?>> answers) { answersForStubbing.addAll(answers); } diff --git a/src/main/java/org/mockito/internal/stubbing/answers/ClonesArguments.java b/src/main/java/org/mockito/internal/stubbing/answers/ClonesArguments.java index 7296095..25e8be7 100644 --- a/src/main/java/org/mockito/internal/stubbing/answers/ClonesArguments.java +++ b/src/main/java/org/mockito/internal/stubbing/answers/ClonesArguments.java @@ -5,7 +5,7 @@ package org.mockito.internal.stubbing.answers; import org.mockito.internal.configuration.plugins.Plugins; -import org.mockito.internal.creation.instance.Instantiator; +import org.mockito.creation.instance.Instantiator; import org.mockito.internal.stubbing.defaultanswers.ReturnsEmptyValues; import org.mockito.internal.util.reflection.LenientCopyTool; import org.mockito.invocation.InvocationOnMock; diff --git a/src/main/java/org/mockito/internal/util/collections/Sets.java b/src/main/java/org/mockito/internal/util/collections/Sets.java index a5a80f1..f1972e9 100644 --- a/src/main/java/org/mockito/internal/util/collections/Sets.java +++ b/src/main/java/org/mockito/internal/util/collections/Sets.java @@ -18,10 +18,6 @@ public abstract class Sets { return HashCodeAndEqualsSafeSet.of(mocks); } - public static IdentitySet newIdentitySet() { - return new IdentitySet(); - } - public static <T> Set<T> newSet(T ... elements) { if (elements == null) { throw new IllegalArgumentException("Expected an array of elements (or empty array) but received a null."); diff --git a/src/main/java/org/mockito/internal/util/concurrent/WeakConcurrentMap.java b/src/main/java/org/mockito/internal/util/concurrent/WeakConcurrentMap.java index c147693..b411a73 100644 --- a/src/main/java/org/mockito/internal/util/concurrent/WeakConcurrentMap.java +++ b/src/main/java/org/mockito/internal/util/concurrent/WeakConcurrentMap.java @@ -26,7 +26,7 @@ public class WeakConcurrentMap<K, V> extends ReferenceQueue<K> implements Runnab private static final AtomicLong ID = new AtomicLong(); - final ConcurrentMap<WeakKey<K>, V> target; + public final ConcurrentMap<WeakKey<K>, V> target; private final Thread thread; diff --git a/src/main/java/org/mockito/invocation/InvocationFactory.java b/src/main/java/org/mockito/invocation/InvocationFactory.java index 8339642..1803718 100644 --- a/src/main/java/org/mockito/invocation/InvocationFactory.java +++ b/src/main/java/org/mockito/invocation/InvocationFactory.java @@ -8,6 +8,7 @@ import org.mockito.Incubating; import org.mockito.MockitoFramework; import org.mockito.mock.MockCreationSettings; +import java.io.Serializable; import java.lang.reflect.Method; import java.util.concurrent.Callable; @@ -19,7 +20,7 @@ import java.util.concurrent.Callable; * <p> * Please don't provide your own implementation of {@link Invocation} type. * Mockito team needs flexibility to add new methods to this interface if we need to. - * If you integrate Mockito framework and you need an instance of {@link Invocation}, use {@link #createInvocation(Object, MockCreationSettings, Method, Callable, Object...)}. + * If you integrate Mockito framework and you need an instance of {@link Invocation}, use {@link #createInvocation(Object, MockCreationSettings, Method, RealMethodBehavior, Object...)}. * * @since 2.10.0 */ @@ -27,6 +28,11 @@ import java.util.concurrent.Callable; public interface InvocationFactory { /** + * @deprecated Use {@link #createInvocation(Object, MockCreationSettings, Method, RealMethodBehavior, Object...)} instead. + * + * Why deprecated? We found use cases where we need to handle Throwable and ensure correct stack trace filtering + * (removing Mockito internals from the stack trace). Hence the introduction of {@link RealMethodBehavior}. + * * Creates instance of an {@link Invocation} object. * This method is useful for framework integrators to programmatically simulate method calls on mocks using {@link MockHandler}. * It enables advanced framework integrations. @@ -40,6 +46,32 @@ public interface InvocationFactory { * @return invocation instance * @since 2.10.0 */ - @Incubating + @Deprecated Invocation createInvocation(Object target, MockCreationSettings settings, Method method, Callable realMethod, Object... args); + + /** + * Behavior of the real method. + * + * @since 2.14.0 + */ + interface RealMethodBehavior<R> extends Serializable { + R call() throws Throwable; + } + + /** + * Creates instance of an {@link Invocation} object. + * This method is useful for framework integrators to programmatically simulate method calls on mocks using {@link MockHandler}. + * It enables advanced framework integrations. + * + * @param target the mock object the method is invoked on. + * @param settings creation settings of the mock object. + * @param method java method invoked on mock. + * @param realMethod real method behavior. Needed for spying / invoking real behavior on mock objects. + * @param args the java method arguments + * + * @return invocation instance + * @since 2.14.0 + */ + @Incubating + Invocation createInvocation(Object target, MockCreationSettings settings, Method method, RealMethodBehavior realMethod, Object... args); } diff --git a/src/main/java/org/mockito/junit/MockitoJUnitRunner.java b/src/main/java/org/mockito/junit/MockitoJUnitRunner.java index 2a15587..59bc92b 100644 --- a/src/main/java/org/mockito/junit/MockitoJUnitRunner.java +++ b/src/main/java/org/mockito/junit/MockitoJUnitRunner.java @@ -4,7 +4,6 @@ */ package org.mockito.junit; -import java.lang.reflect.InvocationTargetException; import org.junit.runner.Description; import org.junit.runner.Runner; import org.junit.runner.manipulation.Filter; @@ -16,12 +15,14 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; import org.mockito.exceptions.misusing.UnnecessaryStubbingException; -import org.mockito.internal.runners.RunnerFactory; import org.mockito.internal.runners.InternalRunner; +import org.mockito.internal.runners.RunnerFactory; import org.mockito.internal.runners.StrictRunner; import org.mockito.quality.MockitoHint; import org.mockito.quality.Strictness; +import java.lang.reflect.InvocationTargetException; + /** * Mockito JUnit Runner keeps tests clean and improves debugging experience. @@ -80,6 +81,7 @@ public class MockitoJUnitRunner extends Runner implements Filterable { * stubbing argument mismatches ({@link MockitoJUnitRunner.StrictStubs}) * and *does not detect* unused stubbings. * The runner remains 'silent' even if incorrect stubbing is present. + * It is an equivalent of {@link Strictness#LENIENT}. * This was the behavior of Mockito JUnit runner in versions 1.x. * Using this implementation of the runner is not recommended. * Engineers should care for removing unused stubbings because they are dead code, diff --git a/src/main/java/org/mockito/plugins/InstantiatorProvider.java b/src/main/java/org/mockito/plugins/InstantiatorProvider.java index b776f6e..354b87b 100644 --- a/src/main/java/org/mockito/plugins/InstantiatorProvider.java +++ b/src/main/java/org/mockito/plugins/InstantiatorProvider.java @@ -8,6 +8,10 @@ import org.mockito.internal.creation.instance.Instantiator; import org.mockito.mock.MockCreationSettings; /** + * @deprecated since 2.15.4 because this internal class was leaking from the public API. + * For more information why deprecated, see {@link org.mockito.plugins.InstantiatorProvider2} and + * <a href="https://github.com/mockito/mockito/issues/1303">Issue 1303</a> + * * <p> * Mockito will invoke this interface in order to fetch an instance instantiator provider. * </p> @@ -44,13 +48,24 @@ import org.mockito.mock.MockCreationSettings; * qualified name in the following file * <code>mockito-extensions/org.mockito.plugins.InstantiatorProvider</code>. * </p> + * <p> + * This class is deprecated and was replaced by + * {@link org.mockito.plugins.InstantiatorProvider2}. Hence if there is both a + * <code>mockito-extensions/org.mockito.plugins.InstantiatorProvider</code> and + * <code>mockito-extensions/org.mockito.plugins.InstantiatorProvider2</code> the second one + * takes preference. + * </p> * - * @since 21.10.15 + * @since 2.0.31 */ +@Deprecated public interface InstantiatorProvider { /** + * @deprecated, see {@link InstantiatorProvider}. + * * Returns an instantiator, used to create new class instances. */ + @Deprecated Instantiator getInstantiator(MockCreationSettings<?> settings); } diff --git a/src/main/java/org/mockito/plugins/InstantiatorProvider2.java b/src/main/java/org/mockito/plugins/InstantiatorProvider2.java new file mode 100644 index 0000000..3bc1f32 --- /dev/null +++ b/src/main/java/org/mockito/plugins/InstantiatorProvider2.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.plugins; + +import org.mockito.creation.instance.Instantiator; +import org.mockito.mock.MockCreationSettings; + +/** + * <p> + * Mockito will invoke this interface in order to fetch an instance instantiator provider. + * </p> + * + * <p> + * By default, an internal byte-buddy/asm/objenesis based implementation is used. + * </p> + * + * <h3>Using the extension point</h3> + * + * <p> + * The plugin mechanism of mockito works in a similar way as the + * {@link java.util.ServiceLoader}, however instead of looking in the <code>META-INF</code> + * directory, Mockito will look in <code>mockito-extensions</code> directory. + * <em>The reason for that is that Android SDK strips jar from the <code>META-INF</code> + * directory when creating an APK.</em> + * </p> + * + * <ol style="list-style-type: lower-alpha"> + * <li>The implementation itself, for example + * <code>org.awesome.mockito.AwesomeInstantiatorProvider2</code> that implements the + * <code>InstantiatorProvider2</code>.</li> + * <li>A file "<code>mockito-extensions/org.mockito.plugins.InstantiatorProvider2</code>". + * The content of this file is exactly a <strong>one</strong> line with the qualified + * name: <code>org.awesome.mockito.AwesomeInstantiatorProvider</code>.</li> + * </ol></p> + * + * <p> + * Note that if several <code>mockito-extensions/org.mockito.plugins.InstantiatorProvider2</code> + * files exists in the classpath, Mockito will only use the first returned by the standard + * {@link ClassLoader#getResource} mechanism. + * <p> + * So just create a custom implementation of {@link InstantiatorProvider2} and place the + * qualified name in the following file + * <code>mockito-extensions/org.mockito.plugins.InstantiatorProvider2</code>. + * </p> + * + * @since 2.15.4 + */ +public interface InstantiatorProvider2 { + + /** + * Returns an instantiator, used to create new class instances. + * + * @since 2.15.4 + */ + Instantiator getInstantiator(MockCreationSettings<?> settings); +} diff --git a/src/main/java/org/mockito/quality/Strictness.java b/src/main/java/org/mockito/quality/Strictness.java index 4e1126a..c9da8d3 100644 --- a/src/main/java/org/mockito/quality/Strictness.java +++ b/src/main/java/org/mockito/quality/Strictness.java @@ -80,7 +80,8 @@ public enum Strictness { * <li>Cleaner, more DRY tests ("Don't Repeat Yourself"): * If you use {@link org.mockito.Mockito#verifyNoMoreInteractions(Object...)} * you no longer need to explicitly verify stubbed invocations. - * They are automatically verified for you.</li> + * They are automatically verified for you. However if you have more invocations, + * the test won't fail since it won't check that there are no more interactions on that stub.</li> * </ul> * * For more information see {@link Strictness}. diff --git a/src/main/java/org/mockito/session/MockitoSessionBuilder.java b/src/main/java/org/mockito/session/MockitoSessionBuilder.java index b13062b..474b7f9 100644 --- a/src/main/java/org/mockito/session/MockitoSessionBuilder.java +++ b/src/main/java/org/mockito/session/MockitoSessionBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Mockito contributors + * Copyright (c) 2018 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.session; @@ -20,7 +20,7 @@ import org.mockito.quality.Strictness; public interface MockitoSessionBuilder { /** - * Configures the test class instance for initialization of fields annotated with Mockito annotations + * Adds the test class instance for initialization of fields annotated with Mockito annotations * like {@link org.mockito.Mock}. * When this method is invoked it <strong>does not perform</strong> initialization of mocks on the spot! * Only when {@link #startMocking()} is invoked then annotated fields will be initialized. @@ -30,11 +30,12 @@ public interface MockitoSessionBuilder { * Migrate from {@link MockitoAnnotations#initMocks(Object)} * to {@link MockitoSession}! * <p> + * This method may be called multiple times to add multiple, e.g. nested, test class instances. + * <p> * See code sample in {@link MockitoSession}. * * @param testClassInstance test class instance that contains fields with Mockito annotations to be initialized. - * Passing {@code null} is permitted and will make the session use a default value. - * The current default is '{@code new Object()}'. + * Passing {@code null} is permitted but will be ignored. * @return the same builder instance for fluent configuration of {@code MockitoSession}. * @since 2.7.0 */ @@ -42,6 +43,46 @@ public interface MockitoSessionBuilder { MockitoSessionBuilder initMocks(Object testClassInstance); /** + * Adds the test class instances for initialization of fields annotated with Mockito annotations + * like {@link org.mockito.Mock}. + * <p> + * In most scenarios, you only need to init mocks on a single test class instance. + * This method is useful for advanced framework integrations (like JUnit5), when a test uses multiple, e.g. nested, test class instances. + * <p> + * This method calls {@link #initMocks(Object)} for each passed test class instance. + * + * @param testClassInstances test class instances that contains fields with Mockito annotations to be initialized. + * Passing {@code null} or an empty array is permitted but will be ignored. + * @return the same builder instance for fluent configuration of {@code MockitoSession}. + * @see #initMocks(Object) + * @since 2.15.0 + */ + @Incubating + MockitoSessionBuilder initMocks(Object... testClassInstances); + + /** + * Configures the name of the {@code MockitoSession} instance. + * <p> + * The name is used to output {@linkplain org.mockito.quality.MockitoHint hints} when + * {@linkplain MockitoSession#finishMocking() finishing} a session. + * <p> + * This method is intended to be used by framework integrations, e.g. JUnit. When building + * a {@code MockitoSession} for direct use, users are not expected to call it. + * + * @param name of {@code MockitoSession} instance. + * Passing {@code null} is permitted and will make the session use a default value. + * The current default is the name of the last test class instance passed to + * {@link #initMocks(Object)} or {@link #initMocks(Object...)}, if available; + * otherwise, {@code "<Unnamed Session>"} is used. + * + * @return the same builder instance for fluent configuration of {@code MockitoSession}. + * @see org.mockito.quality.MockitoHint + * @since 2.15.0 + */ + @Incubating + MockitoSessionBuilder name(String name); + + /** * Configures strictness of {@code MockitoSession} instance. * See examples in {@link MockitoSession}. * @@ -56,6 +97,25 @@ public interface MockitoSessionBuilder { MockitoSessionBuilder strictness(Strictness strictness); /** + * Configures logger used by {@code MockitoSession} for emitting + * {@linkplain org.mockito.quality.MockitoHint warnings} when finishing the session. + * <p> + * Please note that the use of {@linkplain Strictness#STRICT_STUBS strict stubs} is + * recommended over emitting warnings because warnings are easily ignored and spoil the log output. + * Instead of using this method, please consider setting strictness with {@link #strictness(Strictness)}. + * + * @param logger for warnings emitted when finishing {@code MockitoSession}. + * Passing {@code null} is permitted and will make the session use a default value. + * By default, warnings will be logged to the console. + * + * @return the same builder instance for fluent configuration of {@code MockitoSession}. + * @see org.mockito.quality.MockitoHint + * @since 2.15.0 + */ + @Incubating + MockitoSessionBuilder logger(MockitoSessionLogger logger); + + /** * Starts new mocking session! Creates new {@code MockitoSession} instance to initialize the session. * At this point annotated fields are initialized per {@link #initMocks(Object)} method. * When you are done with the session it is required to invoke {@link MockitoSession#finishMocking()}. diff --git a/src/main/java/org/mockito/session/MockitoSessionLogger.java b/src/main/java/org/mockito/session/MockitoSessionLogger.java new file mode 100644 index 0000000..e5f40ad --- /dev/null +++ b/src/main/java/org/mockito/session/MockitoSessionLogger.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.session; + +import org.mockito.Incubating; +import org.mockito.MockitoSession; + +/** + * Logger for {@linkplain org.mockito.quality.MockitoHint hints} emitted when + * finishing mocking for a {@link MockitoSession}. + * <p> + * This class is intended to be used by framework integrations, e.g. JUnit. When using + * {@link MockitoSession} directly, you'll probably not need it. + * + * @since 2.15.0 + */ +@Incubating +public interface MockitoSessionLogger { + + /** + * Logs the hint. + * + * @param hint to log; never {@code null} + */ + @Incubating + void log(String hint); + +} diff --git a/src/main/java/org/mockito/stubbing/OngoingStubbing.java b/src/main/java/org/mockito/stubbing/OngoingStubbing.java index bc768d2..e2d298b 100644 --- a/src/main/java/org/mockito/stubbing/OngoingStubbing.java +++ b/src/main/java/org/mockito/stubbing/OngoingStubbing.java @@ -43,7 +43,7 @@ public interface OngoingStubbing<T> { * * @param value return value * - * @return iOngoingStubbing object that allows stubbing consecutive calls + * @return object that allows stubbing consecutive calls */ OngoingStubbing<T> thenReturn(T value); @@ -60,7 +60,7 @@ public interface OngoingStubbing<T> { * @param value first return value * @param values next return values * - * @return iOngoingStubbing object that allows stubbing consecutive calls + * @return object that allows stubbing consecutive calls */ // Additional method helps users of JDK7+ to hide heap pollution / unchecked generics array creation warnings (on call site) @SuppressWarnings ({"unchecked", "varargs"}) @@ -84,7 +84,7 @@ public interface OngoingStubbing<T> { * * @param throwables to be thrown on method invocation * - * @return iOngoingStubbing object that allows stubbing consecutive calls + * @return object that allows stubbing consecutive calls */ OngoingStubbing<T> thenThrow(Throwable... throwables); @@ -108,7 +108,7 @@ public interface OngoingStubbing<T> { * * @param throwableType to be thrown on method invocation * - * @return iOngoingStubbing object that allows stubbing consecutive calls + * @return object that allows stubbing consecutive calls * @since 2.1.0 */ OngoingStubbing<T> thenThrow(Class<? extends Throwable> throwableType); @@ -141,7 +141,7 @@ public interface OngoingStubbing<T> { * @param toBeThrown to be thrown on method invocation * @param nextToBeThrown next to be thrown on method invocation * - * @return iOngoingStubbing object that allows stubbing consecutive calls + * @return object that allows stubbing consecutive calls * @since 2.1.0 */ // Additional method helps users of JDK7+ to hide heap pollution / unchecked generics array creation warnings (on call site) @@ -175,7 +175,7 @@ public interface OngoingStubbing<T> { * <p> * See examples in javadoc for {@link Mockito#when} * - * @return iOngoingStubbing object that allows stubbing consecutive calls + * @return object that allows stubbing consecutive calls */ OngoingStubbing<T> thenCallRealMethod(); @@ -191,7 +191,7 @@ public interface OngoingStubbing<T> { * * @param answer the custom answer to execute. * - * @return iOngoingStubbing object that allows stubbing consecutive calls + * @return object that allows stubbing consecutive calls */ OngoingStubbing<T> thenAnswer(Answer<?> answer); @@ -209,7 +209,7 @@ public interface OngoingStubbing<T> { * </code></pre> * * @param answer the custom answer to execute. - * @return iOngoingStubbing object that allows stubbing consecutive calls + * @return object that allows stubbing consecutive calls * * @see #thenAnswer(Answer) * @since 1.9.0 diff --git a/src/test/java/org/mockito/InvocationFactoryTest.java b/src/test/java/org/mockito/InvocationFactoryTest.java new file mode 100644 index 0000000..893f95f --- /dev/null +++ b/src/test/java/org/mockito/InvocationFactoryTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018 Mockito contributors + * This program is made available under the terms of the MIT License. + */ + +package org.mockito; + +import org.junit.Test; +import org.mockito.invocation.Invocation; +import org.mockito.invocation.InvocationFactory; +import org.mockitoutil.TestBase; + +import java.util.concurrent.Callable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.withSettings; + +public class InvocationFactoryTest extends TestBase { + static class TestClass { + public String testMethod() throws Throwable { + return "un-mocked"; + } + } + + final TestClass mock = spy(TestClass.class); + + @Test + public void call_method_that_throws_a_throwable() throws Throwable { + Invocation invocation = Mockito.framework().getInvocationFactory().createInvocation(mock, + withSettings().build(TestClass.class), + TestClass.class.getDeclaredMethod("testMethod"), + new InvocationFactory.RealMethodBehavior() { + @Override + public Object call() throws Throwable { + throw new Throwable("mocked"); + } + }); + + try { + Mockito.mockingDetails(mock).getMockHandler().handle(invocation); + } catch (Throwable t) { + assertEquals("mocked", t.getMessage()); + return; + } + + fail(); + } + + @Test + public void call_method_that_returns_a_string() throws Throwable { + Invocation invocation = Mockito.framework().getInvocationFactory().createInvocation(mock, + withSettings().build(TestClass.class), + TestClass.class.getDeclaredMethod("testMethod"), + new InvocationFactory.RealMethodBehavior() { + @Override + public Object call() throws Throwable { + return "mocked"; + } + }); + + Object ret = Mockito.mockingDetails(mock).getMockHandler().handle(invocation); + assertEquals("mocked", ret); + } + + @Test + public void deprecated_api_still_works() throws Throwable { + Invocation invocation = Mockito.framework().getInvocationFactory().createInvocation(mock, + withSettings().build(TestClass.class), + TestClass.class.getDeclaredMethod("testMethod"), + new Callable() { + public Object call() throws Exception { + return "mocked"; + } + }); + + Object ret = Mockito.mockingDetails(mock).getMockHandler().handle(invocation); + assertEquals("mocked", ret); + } +} diff --git a/src/test/java/org/mockito/StaticMockingExperimentTest.java b/src/test/java/org/mockito/StaticMockingExperimentTest.java index a74d5eb..bb5e225 100644 --- a/src/test/java/org/mockito/StaticMockingExperimentTest.java +++ b/src/test/java/org/mockito/StaticMockingExperimentTest.java @@ -10,12 +10,12 @@ import org.mockito.exceptions.verification.NoInteractionsWanted; import org.mockito.exceptions.verification.WantedButNotInvoked; import org.mockito.exceptions.verification.junit.ArgumentsAreDifferent; import org.mockito.invocation.Invocation; +import org.mockito.invocation.InvocationFactory; import org.mockito.invocation.MockHandler; import org.mockitoutil.TestBase; import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import java.util.concurrent.Callable; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; @@ -43,9 +43,9 @@ public class StaticMockingExperimentTest extends TestBase { Foo mock = Mockito.mock(Foo.class); MockHandler handler = Mockito.mockingDetails(mock).getMockHandler(); Method staticMethod; - Callable realMethod = new Callable() { + InvocationFactory.RealMethodBehavior realMethod = new InvocationFactory.RealMethodBehavior() { @Override - public Object call() throws Exception { + public Object call() throws Throwable { return null; } }; diff --git a/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java b/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java index 41fe3cc..5935eae 100644 --- a/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java +++ b/src/test/java/org/mockito/internal/configuration/plugins/DefaultMockitoPluginsTest.java @@ -7,6 +7,8 @@ package org.mockito.internal.configuration.plugins; import org.junit.Test; import org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker; import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker; +import org.mockito.plugins.InstantiatorProvider; +import org.mockito.plugins.InstantiatorProvider2; import org.mockito.plugins.MockMaker; import org.mockitoutil.TestBase; @@ -23,5 +25,7 @@ public class DefaultMockitoPluginsTest extends TestBase { plugins.getDefaultPluginClass(INLINE_ALIAS)); assertEquals(InlineByteBuddyMockMaker.class, plugins.getInlineMockMaker().getClass()); assertEquals(ByteBuddyMockMaker.class, plugins.getDefaultPlugin(MockMaker.class).getClass()); + assertNotNull(plugins.getDefaultPlugin(InstantiatorProvider.class)); + assertNotNull(plugins.getDefaultPlugin(InstantiatorProvider2.class)); } } diff --git a/src/test/java/org/mockito/internal/configuration/plugins/PluginLoaderTest.java b/src/test/java/org/mockito/internal/configuration/plugins/PluginLoaderTest.java new file mode 100644 index 0000000..ab01858 --- /dev/null +++ b/src/test/java/org/mockito/internal/configuration/plugins/PluginLoaderTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.configuration.plugins; + +import org.assertj.core.api.Assertions; +import org.assertj.core.api.ThrowableAssert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.quality.Strictness; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.when; + +public class PluginLoaderTest { + + @Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); + + @Mock PluginInitializer initializer; + @Mock DefaultMockitoPlugins plugins; + @InjectMocks PluginLoader loader; + + @Test + public void loads_plugin() { + when(initializer.loadImpl(FooPlugin.class)).thenReturn(new FooPlugin()); + + //when + FooPlugin plugin = loader.loadPlugin(FooPlugin.class); + + //then + assertNotNull(plugin); + } + + @Test + public void loads_alternative_plugin() { + willReturn(null).given(initializer).loadImpl(FooPlugin.class); + BarPlugin expected = new BarPlugin(); + willReturn(expected).given(initializer).loadImpl(BarPlugin.class); + + //when + Object plugin = loader.loadPlugin(FooPlugin.class, BarPlugin.class); + + //then + assertSame(plugin, expected); + } + + @Test + public void loads_default_plugin() { + willReturn(null).given(initializer).loadImpl(FooPlugin.class); + willReturn(null).given(initializer).loadImpl(BarPlugin.class); + FooPlugin expected = new FooPlugin(); + willReturn(expected).given(plugins).getDefaultPlugin(FooPlugin.class); + + //when + Object plugin = loader.loadPlugin(FooPlugin.class, BarPlugin.class); + + //then + assertSame(plugin, expected); + } + + @Test + public void fails_to_load_plugin() { + RuntimeException cause = new RuntimeException("Boo!"); + when(initializer.loadImpl(Foo.class)).thenThrow(cause); + + //when + final Foo plugin = loader.loadPlugin(Foo.class); + + //then + Assertions.assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + plugin.toString(); //call any method on the plugin + } + }).isInstanceOf(IllegalStateException.class) + .hasMessage("Could not initialize plugin: interface org.mockito.internal.configuration.plugins.PluginLoaderTest$Foo (alternate: null)") + .hasCause(cause); + } + + static class FooPlugin {} + static class BarPlugin {} + static interface Foo {} +} diff --git a/src/test/java/org/mockito/internal/creation/instance/ConstructorInstantiatorTest.java b/src/test/java/org/mockito/internal/creation/instance/ConstructorInstantiatorTest.java index 50030df..320287c 100644 --- a/src/test/java/org/mockito/internal/creation/instance/ConstructorInstantiatorTest.java +++ b/src/test/java/org/mockito/internal/creation/instance/ConstructorInstantiatorTest.java @@ -65,7 +65,7 @@ public class ConstructorInstantiatorTest extends TestBase { assertEquals(new ConstructorInstantiator(false, 123).newInstance(SomeClass3.class).getClass(), SomeClass3.class); } - @Test(expected = InstantiationException.class) + @Test(expected = org.mockito.creation.instance.InstantiationException.class) public void fails_when_null_is_passed_for_a_primitive() { assertEquals(new ConstructorInstantiator(false, new Object[]{null}).newInstance(SomeClass3.class).getClass(), SomeClass3.class); } @@ -75,7 +75,7 @@ public class ConstructorInstantiatorTest extends TestBase { try { new ConstructorInstantiator(false, new Object[0]).newInstance(SomeClass2.class); fail(); - } catch (InstantiationException e) { + } catch (org.mockito.creation.instance.InstantiationException e) { assertThat(e).hasMessageContaining("Unable to create instance of 'SomeClass2'.\n" + "Please ensure that the target class has a 0-arg constructor."); } diff --git a/src/test/java/org/mockito/internal/invocation/InvocationBuilder.java b/src/test/java/org/mockito/internal/invocation/InvocationBuilder.java index 696d34a..b3106a6 100644 --- a/src/test/java/org/mockito/internal/invocation/InvocationBuilder.java +++ b/src/test/java/org/mockito/internal/invocation/InvocationBuilder.java @@ -6,7 +6,7 @@ package org.mockito.internal.invocation; import org.mockito.Mockito; -import org.mockito.internal.creation.bytebuddy.InterceptedInvocation; +import org.mockito.internal.invocation.mockref.MockStrongReference; import org.mockito.internal.debugging.LocationImpl; import org.mockito.invocation.Invocation; import org.mockito.invocation.Location; @@ -17,7 +17,7 @@ import java.util.LinkedList; import java.util.List; import static java.util.Arrays.asList; -import static org.mockito.internal.creation.bytebuddy.InterceptedInvocation.NO_OP; +import static org.mockito.internal.invocation.InterceptedInvocation.NO_OP; /** * Build an invocation. @@ -61,7 +61,7 @@ public class InvocationBuilder { } } - Invocation i = new InterceptedInvocation(mock, + Invocation i = new InterceptedInvocation(new MockStrongReference<Object>(mock, false), new SerializableMethod(method), args, NO_OP, diff --git a/src/test/java/org/mockito/internal/invocation/mockref/MockWeakReferenceTest.java b/src/test/java/org/mockito/internal/invocation/mockref/MockWeakReferenceTest.java new file mode 100644 index 0000000..1e3d8e9 --- /dev/null +++ b/src/test/java/org/mockito/internal/invocation/mockref/MockWeakReferenceTest.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.invocation.mockref; + +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.mockito.internal.invocation.mockref.MockWeakReference; +import org.mockitoutil.TestBase; + +import static org.junit.Assert.fail; + +public class MockWeakReferenceTest extends TestBase { + + @Test + public void descriptive_exception_when_mock_was_collected() { + try { + //when + new MockWeakReference(null).get(); + //then + fail(); + } catch (Exception e) { + Assertions.assertThat(e).hasMessageContaining("The mock object was garbage collected"); + } + } +} diff --git a/src/test/java/org/mockito/internal/session/DefaultMockitoSessionBuilderTest.java b/src/test/java/org/mockito/internal/session/DefaultMockitoSessionBuilderTest.java index f519186..855afb0 100644 --- a/src/test/java/org/mockito/internal/session/DefaultMockitoSessionBuilderTest.java +++ b/src/test/java/org/mockito/internal/session/DefaultMockitoSessionBuilderTest.java @@ -1,29 +1,42 @@ /* - * Copyright (c) 2017 Mockito contributors + * Copyright (c) 2018 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.session; import org.junit.After; import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoSession; import org.mockito.StateMaster; import org.mockito.exceptions.misusing.UnfinishedMockingSessionException; import org.mockito.quality.Strictness; +import org.mockito.session.MockitoSessionLogger; import org.mockitoutil.ThrowableAssert; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; +import static org.mockito.quality.Strictness.WARN; + public class DefaultMockitoSessionBuilderTest { @After public void after() { new StateMaster().clearMockitoListeners(); } - @Test public void creates_sessions() throws Exception { + @Test public void creates_sessions() { //no configuration is legal new DefaultMockitoSessionBuilder().startMocking().finishMocking(); //passing null to configuration is legal, default value will be used - new DefaultMockitoSessionBuilder().initMocks(null).startMocking().finishMocking(); - new DefaultMockitoSessionBuilder().initMocks(null).strictness(null).startMocking().finishMocking(); + new DefaultMockitoSessionBuilder().initMocks((Object) null).startMocking().finishMocking(); + new DefaultMockitoSessionBuilder().initMocks((Object[]) null).startMocking().finishMocking(); + new DefaultMockitoSessionBuilder().initMocks(null, null).strictness(null).startMocking().finishMocking(); new DefaultMockitoSessionBuilder().strictness(null).startMocking().finishMocking(); //happy path @@ -32,7 +45,49 @@ public class DefaultMockitoSessionBuilderTest { new DefaultMockitoSessionBuilder().strictness(Strictness.LENIENT).startMocking().finishMocking(); } - @Test public void requires_finish_mocking() throws Exception { + @Test public void creates_sessions_for_multiple_test_class_instances_for_repeated_calls() { + TestClass testClass = new TestClass(); + TestClass.NestedTestClass nestedTestClass = testClass.new NestedTestClass(); + + new DefaultMockitoSessionBuilder().initMocks(testClass).initMocks(nestedTestClass).startMocking().finishMocking(); + + assertNotNull(testClass.set); + assertNotNull(nestedTestClass.list); + } + + @Test public void creates_sessions_for_multiple_test_class_instances_for_varargs_call() { + TestClass testClass = new TestClass(); + TestClass.NestedTestClass nestedTestClass = testClass.new NestedTestClass(); + + new DefaultMockitoSessionBuilder().initMocks(testClass, nestedTestClass).startMocking().finishMocking(); + + assertNotNull(testClass.set); + assertNotNull(nestedTestClass.list); + } + + @Test public void uses_logger_and_strictness() { + TestClass testClass = new TestClass(); + + final List<String> hints = new ArrayList<String>(); + MockitoSession session = new DefaultMockitoSessionBuilder() + .initMocks(testClass) + .strictness(WARN) + .logger(new MockitoSessionLogger() { + @Override + public void log(String hint) { + hints.add(hint); + } + }) + .startMocking(); + + when(testClass.set.add(1)).thenReturn(true); + + session.finishMocking(); + + assertFalse(hints.isEmpty()); + } + + @Test public void requires_finish_mocking() { new DefaultMockitoSessionBuilder().startMocking(); ThrowableAssert.assertThat(new Runnable() { @@ -41,4 +96,13 @@ public class DefaultMockitoSessionBuilderTest { } }).throwsException(UnfinishedMockingSessionException.class); } + + class TestClass { + + @Mock public Set<Object> set; + + class NestedTestClass { + @Mock public List<Object> list; + } + } } diff --git a/src/test/java/org/mockito/internal/stubbing/InvocationContainerImplStubbingTest.java b/src/test/java/org/mockito/internal/stubbing/InvocationContainerImplStubbingTest.java index 0e77824..de5eb13 100644 --- a/src/test/java/org/mockito/internal/stubbing/InvocationContainerImplStubbingTest.java +++ b/src/test/java/org/mockito/internal/stubbing/InvocationContainerImplStubbingTest.java @@ -5,8 +5,6 @@ package org.mockito.internal.stubbing; -import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress; - import org.junit.Before; import org.junit.Test; import org.mockito.exceptions.base.MockitoException; @@ -21,6 +19,7 @@ import org.mockitoutil.TestBase; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress; public class InvocationContainerImplStubbingTest extends TestBase { @@ -96,27 +95,6 @@ public class InvocationContainerImplStubbingTest extends TestBase { } @Test - public void should_add_throwable_for_void_method() throws Throwable { - invocationContainerImpl.addAnswerForVoidMethod(new ThrowsException(new MyException())); - invocationContainerImpl.setMethodForStubbing(new InvocationMatcher(simpleMethod)); - - try { - invocationContainerImpl.answerTo(simpleMethod); - fail(); - } catch (MyException e) {} - } - - @Test - public void should_validate_throwable_for_void_method() throws Throwable { - invocationContainerImpl.addAnswerForVoidMethod(new ThrowsException(new Exception())); - - try { - invocationContainerImpl.setMethodForStubbing(new InvocationMatcher(simpleMethod)); - fail(); - } catch (MockitoException e) {} - } - - @Test public void should_validate_throwable() throws Throwable { try { invocationContainerImpl.addAnswer(new ThrowsException(null)); diff --git a/src/test/java/org/mockitousage/bugs/MockitoStubbedCallInAnswerTest.java b/src/test/java/org/mockitousage/bugs/MockitoStubbedCallInAnswerTest.java new file mode 100644 index 0000000..d9720f9 --- /dev/null +++ b/src/test/java/org/mockitousage/bugs/MockitoStubbedCallInAnswerTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitousage.bugs; + +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.mockitoutil.TestBase; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +/** + * @see <a href="https://github.com/mockito/mockito/issues/1279">Issue #1279</a> + */ +public class MockitoStubbedCallInAnswerTest extends TestBase { + + @Mock Foo foo; + @Mock Bar bar; + + @Test + public void stubbing_the_right_mock() throws Exception { + //stubbing on different mock should not be altered + when(bar.doInt()).thenReturn(0); + when(foo.doInt()).thenAnswer(new Answer<Integer>() { + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + return bar.doInt(); + } + }); + assertEquals(0, foo.doInt()); + assertEquals(0, bar.doInt()); + + //when we override the stubbing + when(foo.doInt()).thenReturn(1); + + //we expect it to be reflected: + assertEquals(1, foo.doInt()); + + //but the stubbing on a different mock should not be altered: + assertEquals(0, bar.doInt()); + } + + @Test + public void return_type_validation() throws Exception { + when(foo.doString()).thenAnswer(new Answer<String>() { + public String answer(InvocationOnMock invocation) throws Throwable { + //invoking a method on a different mock, with different return type + return String.valueOf(bar.doInt()); + } + }); + assertEquals("0", foo.doString()); + + //we can override stubbing without misleading return type validation errors: + when(foo.doString()).thenReturn(""); + assertEquals("", foo.doString()); + } + + @Test + public void prevents_stack_overflow() throws Exception { + when(foo.doInt()).thenAnswer(new Answer<Integer>() { + public Integer answer(InvocationOnMock invocation) throws Throwable { + return bar.doInt(); + } + }); + assertEquals(0, foo.doInt()); + + when(foo.doInt()).thenAnswer(new Answer<Integer>() { + public Integer answer(InvocationOnMock invocation) throws Throwable { + return bar.doInt() + 1; + } + }); + + //calling below used to cause SO error + assertEquals(1, foo.doInt()); + } + + @Test + public void overriding_stubbing() throws Exception { + when(bar.doInt()).thenReturn(10); + when(foo.doInt()).thenAnswer(new Answer<Integer>() { + public Integer answer(InvocationOnMock invocation) throws Throwable { + return bar.doInt() + 1; + } + }); + + assertEquals(11, foo.doInt()); + + //when we override the stubbing with a different one + when(foo.doInt()).thenReturn(100); + + //we expect it to be reflected: + assertEquals(100, foo.doInt()); + } + + interface Foo { + String doString(); + int doInt(); + } + + interface Bar { + int doInt(); + } +} diff --git a/src/test/java/org/mockitousage/junitrule/StrictJUnitRuleTest.java b/src/test/java/org/mockitousage/junitrule/StrictJUnitRuleTest.java index 59e5999..f4e3e95 100644 --- a/src/test/java/org/mockitousage/junitrule/StrictJUnitRuleTest.java +++ b/src/test/java/org/mockitousage/junitrule/StrictJUnitRuleTest.java @@ -8,18 +8,20 @@ import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; -import org.mockito.quality.Strictness; import org.mockito.exceptions.misusing.PotentialStubbingProblem; import org.mockito.exceptions.misusing.UnfinishedVerificationException; import org.mockito.exceptions.misusing.UnnecessaryStubbingException; import org.mockito.junit.MockitoJUnit; +import org.mockito.quality.Strictness; import org.mockitousage.IMethods; import org.mockitoutil.SafeJUnitRule; import static org.junit.Assert.assertEquals; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willReturn; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; import static org.mockitoutil.TestBase.filterLineNo; public class StrictJUnitRuleTest { @@ -97,7 +99,7 @@ public class StrictJUnitRuleTest { " - stubbing the same method multiple times using 'given().will()' or 'when().then()' API\n" + " Please use 'will().given()' or 'doReturn().when()' API for stubbing.\n" + " - stubbed method is intentionally invoked with different arguments by code under test\n" + - " Please use 'default' or 'silent' JUnit Rule.\n" + + " Please use default or 'silent' JUnit Rule (equivalent of Strictness.LENIENT).\n" + "For more information see javadoc for PotentialStubbingProblem class."), filterLineNo(t.getMessage())); } @@ -140,7 +142,7 @@ public class StrictJUnitRuleTest { "Following stubbings are unnecessary (click to navigate to relevant line of code):\n" + " 1. -> at org.mockitousage.junitrule.StrictJUnitRuleTest.unused_stubs_with_multiple_mocks(StrictJUnitRuleTest.java:0)\n" + " 2. -> at org.mockitousage.junitrule.StrictJUnitRuleTest.unused_stubs_with_multiple_mocks(StrictJUnitRuleTest.java:0)\n" + - "Please remove unnecessary stubbings or use 'silent' option. More info: javadoc for UnnecessaryStubbingException class."), filterLineNo(t.getMessage())); + "Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class."), filterLineNo(t.getMessage())); } }); diff --git a/src/test/java/org/mockitousage/junitrunner/UnusedStubsExceptionMessageTest.java b/src/test/java/org/mockitousage/junitrunner/UnusedStubsExceptionMessageTest.java index 0d8cead..6b6017a 100644 --- a/src/test/java/org/mockitousage/junitrunner/UnusedStubsExceptionMessageTest.java +++ b/src/test/java/org/mockitousage/junitrunner/UnusedStubsExceptionMessageTest.java @@ -53,7 +53,7 @@ public class UnusedStubsExceptionMessageTest extends TestBase { "Following stubbings are unnecessary (click to navigate to relevant line of code):\n" + " 1. -> at org.mockitousage.junitrunner.UnusedStubsExceptionMessageTest$HasUnnecessaryStubs.<init>(UnusedStubsExceptionMessageTest.java:0)\n" + " 2. -> at org.mockitousage.junitrunner.UnusedStubsExceptionMessageTest$HasUnnecessaryStubs.<init>(UnusedStubsExceptionMessageTest.java:0)\n" + - "Please remove unnecessary stubbings or use 'silent' option. More info: javadoc for UnnecessaryStubbingException class.", - filterLineNo(failure.getException().getMessage())); + "Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.", + filterLineNo(failure.getException().getMessage())); } } diff --git a/src/test/java/org/mockitousage/plugins/MockitoPluginsTest.java b/src/test/java/org/mockitousage/plugins/MockitoPluginsTest.java index 0d164c6..a1a3500 100644 --- a/src/test/java/org/mockitousage/plugins/MockitoPluginsTest.java +++ b/src/test/java/org/mockitousage/plugins/MockitoPluginsTest.java @@ -6,8 +6,10 @@ package org.mockitousage.plugins; import org.junit.Test; import org.mockito.Mockito; +import org.mockito.internal.creation.instance.Instantiator; import org.mockito.plugins.AnnotationEngine; import org.mockito.plugins.InstantiatorProvider; +import org.mockito.plugins.InstantiatorProvider2; import org.mockito.plugins.MockMaker; import org.mockito.plugins.MockitoPlugins; import org.mockito.plugins.PluginSwitch; @@ -15,17 +17,29 @@ import org.mockito.plugins.StackTraceCleanerProvider; import org.mockitoutil.TestBase; import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.withSettings; public class MockitoPluginsTest extends TestBase { private final MockitoPlugins plugins = Mockito.framework().getPlugins(); - @Test public void provides_built_in_plugins() { + @Test + public void provides_built_in_plugins() { assertNotNull(plugins.getInlineMockMaker()); assertNotNull(plugins.getDefaultPlugin(MockMaker.class)); assertNotNull(plugins.getDefaultPlugin(StackTraceCleanerProvider.class)); assertNotNull(plugins.getDefaultPlugin(PluginSwitch.class)); assertNotNull(plugins.getDefaultPlugin(InstantiatorProvider.class)); + assertNotNull(plugins.getDefaultPlugin(InstantiatorProvider2.class)); assertNotNull(plugins.getDefaultPlugin(AnnotationEngine.class)); } + + @SuppressWarnings("deprecation") + @Test + public void instantiator_provider_backwards_compatibility() { + InstantiatorProvider provider = plugins.getDefaultPlugin(InstantiatorProvider.class); + Instantiator instantiator = provider.getInstantiator(withSettings().build(MockitoPluginsTest.class)); + + assertNotNull(instantiator.newInstance(MockitoPluginsTest.class)); + } } diff --git a/src/test/java/org/mockitousage/session/MockitoSessionTest.java b/src/test/java/org/mockitousage/session/MockitoSessionTest.java index e992f83..960d888 100644 --- a/src/test/java/org/mockitousage/session/MockitoSessionTest.java +++ b/src/test/java/org/mockitousage/session/MockitoSessionTest.java @@ -15,13 +15,17 @@ import org.mockito.exceptions.misusing.UnfinishedStubbingException; import org.mockito.quality.Strictness; import org.mockitousage.IMethods; import org.mockitoutil.JUnitResultAssert; +import org.mockitoutil.TestBase; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mockingDetails; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class MockitoSessionTest { +public class MockitoSessionTest extends TestBase { private JUnitCore junit = new JUnitCore(); @@ -66,6 +70,33 @@ public class MockitoSessionTest { .failsExactly(AssertionError.class, UnfinishedStubbingException.class); } + @Test public void allows_initializing_mocks_manually() { + //when + Result result = junit.run(MockitoSessionTest.SessionWithManuallyInitializedMock.class); + + //expect + JUnitResultAssert.assertThat(result).succeeds(1); + } + + @Test public void allows_updating_strictness() { + //when + Result result = junit.run(MockitoSessionTest.SessionWithUpdatedStrictness.class); + + //expect + JUnitResultAssert.assertThat(result).succeeds(1); + } + + @Test public void allows_overriding_failure() { + //when + Result result = junit.run(MockitoSessionTest.SessionWithOverriddenFailure.class); + + //expect + JUnitResultAssert.assertThat(result).isSuccessful(); + + //in order to demonstrate feature, we intentionally misuse Mockito and need to clean up state + resetState(); + } + public static class SessionWithoutAnyConfiguration { @Mock IMethods mock; @@ -139,4 +170,52 @@ public class MockitoSessionTest { assertTrue(false); } } + + public static class SessionWithManuallyInitializedMock { + @Mock IMethods mock; + IMethods mock2 = Mockito.mock(IMethods.class, "manual mock"); + + MockitoSession mockito = Mockito.mockitoSession().initMocks(this).startMocking(); + + @After public void after() { + mockito.finishMocking(); + } + + @Test public void manual_mock_preserves_its_settings() { + assertEquals("mock", mockingDetails(mock).getMockCreationSettings().getMockName().toString()); + assertEquals("manual mock", mockingDetails(mock2).getMockCreationSettings().getMockName().toString()); + } + } + + public static class SessionWithUpdatedStrictness { + @Mock IMethods mock; + MockitoSession mockito = Mockito.mockitoSession().initMocks(this).strictness(Strictness.STRICT_STUBS).startMocking(); + + @After public void after() { + mockito.finishMocking(); + } + + @Test public void manual_mock_preserves_its_settings() { + when(mock.simpleMethod(1)).thenReturn("foo"); + + //when + mockito.setStrictness(Strictness.LENIENT); + + //then no exception is thrown, even though the arg is different + mock.simpleMethod(2); + } + } + + public static class SessionWithOverriddenFailure { + @Mock IMethods mock; + MockitoSession mockito = Mockito.mockitoSession().initMocks(this).startMocking(); + + @After public void after() { + mockito.finishMocking(new RuntimeException("Boo!")); + } + + @Test public void invalid_mockito_usage() { + verify(mock); + } + } } diff --git a/src/test/java/org/mockitousage/stubbing/StubbingWarningsTest.java b/src/test/java/org/mockitousage/stubbing/StubbingWarningsTest.java index 74acd78..754b7dc 100644 --- a/src/test/java/org/mockitousage/stubbing/StubbingWarningsTest.java +++ b/src/test/java/org/mockitousage/stubbing/StubbingWarningsTest.java @@ -1,27 +1,40 @@ /* - * Copyright (c) 2017 Mockito contributors + * Copyright (c) 2018 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockitousage.stubbing; +import org.junit.After; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoSession; +import org.mockito.StateMaster; +import org.mockito.exceptions.base.MockitoException; import org.mockito.internal.framework.DefaultMockitoSession; import org.mockito.internal.util.SimpleMockitoLogger; import org.mockito.quality.Strictness; import org.mockitousage.IMethods; +import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; import static org.mockitoutil.TestBase.filterLineNo; public class StubbingWarningsTest { + private static final String TEST_NAME = "test.name"; + @Mock IMethods mock; SimpleMockitoLogger logger = new SimpleMockitoLogger(); - MockitoSession mockito = new DefaultMockitoSession(this, Strictness.WARN, logger); + MockitoSession mockito = new DefaultMockitoSession(singletonList((Object) this), TEST_NAME, Strictness.WARN, logger); + + @After public void after() { + StateMaster stateMaster = new StateMaster(); + stateMaster.reset(); + stateMaster.clearMockitoListeners(); + } @Test public void few_interactions() throws Throwable { //when @@ -65,7 +78,7 @@ public class StubbingWarningsTest { //because it was simpler to implement. This can be improved given we put priority to improve the warnings. //then assertEquals(filterLineNo( - "[MockitoHint] StubbingWarningsTest.null (see javadoc for MockitoHint):\n" + + "[MockitoHint] " + TEST_NAME + " (see javadoc for MockitoHint):\n" + "[MockitoHint] 1. Unused -> at org.mockitousage.stubbing.StubbingWarningsTest.stubbing_argument_mismatch(StubbingWarningsTest.java:0)\n"), filterLineNo(logger.getLoggedInfo())); } @@ -78,8 +91,25 @@ public class StubbingWarningsTest { //then assertEquals(filterLineNo( - "[MockitoHint] StubbingWarningsTest.null (see javadoc for MockitoHint):\n" + + "[MockitoHint] " + TEST_NAME + " (see javadoc for MockitoHint):\n" + "[MockitoHint] 1. Unused -> at org.mockitousage.stubbing.StubbingWarningsTest.unused_stubbing(StubbingWarningsTest.java:0)\n"), filterLineNo(logger.getLoggedInfo())); } + + @Test(expected = MockitoException.class) public void unfinished_verification_without_throwable() throws Throwable { + //when + verify(mock); + + mockito.finishMocking(); + } + + @Test public void unfinished_verification_with_throwable() throws Throwable { + //when + verify(mock); + + mockito.finishMocking(new AssertionError()); + + // then + logger.assertEmpty(); + } } diff --git a/src/test/java/org/mockitoutil/TestBase.java b/src/test/java/org/mockitoutil/TestBase.java index 2971ee5..ca3d7b8 100644 --- a/src/test/java/org/mockitoutil/TestBase.java +++ b/src/test/java/org/mockitoutil/TestBase.java @@ -11,7 +11,8 @@ import org.mockito.MockitoAnnotations; import org.mockito.StateMaster; import org.mockito.internal.MockitoCore; import org.mockito.internal.configuration.ConfigurationAccess; -import org.mockito.internal.creation.bytebuddy.InterceptedInvocation; +import org.mockito.internal.invocation.mockref.MockStrongReference; +import org.mockito.internal.invocation.InterceptedInvocation; import org.mockito.internal.debugging.LocationImpl; import org.mockito.internal.invocation.InvocationBuilder; import org.mockito.internal.invocation.InvocationMatcher; @@ -64,8 +65,9 @@ public class TestBase { for (int i = 0; i < args.length; i++) { types[i] = args[i].getClass(); } - return new InterceptedInvocation(mock(type), new SerializableMethod(type.getMethod(methodName, - types)), args, InterceptedInvocation.NO_OP, new LocationImpl(), 1); + return new InterceptedInvocation(new MockStrongReference<Object>(mock(type), false), + new SerializableMethod(type.getMethod(methodName, types)), args, InterceptedInvocation.NO_OP, + new LocationImpl(), 1); } protected static Invocation invocationAt(String location) { diff --git a/stub/java/org/mockito/internal/invocation/DefaultInvocationFactory.java b/stub/java/org/mockito/internal/invocation/DefaultInvocationFactory.java deleted file mode 100644 index c9b8a7c..0000000 --- a/stub/java/org/mockito/internal/invocation/DefaultInvocationFactory.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.mockito.internal.invocation; - -import org.mockito.invocation.Invocation; -import org.mockito.invocation.InvocationFactory; -import org.mockito.mock.MockCreationSettings; - -import java.lang.reflect.Method; -import java.util.concurrent.Callable; - -public class DefaultInvocationFactory implements InvocationFactory { - - public Invocation createInvocation(Object target, MockCreationSettings settings, Method method, Callable realMethod, Object... args) { - throw new RuntimeException("Not implemented"); - } -} diff --git a/subprojects/inline/inline.gradle b/subprojects/inline/inline.gradle index be10361..b8d2577 100644 --- a/subprojects/inline/inline.gradle +++ b/subprojects/inline/inline.gradle @@ -8,3 +8,6 @@ dependencies { } tasks.javadoc.enabled = false + +//required by the "StressTest.java" +test.maxHeapSize = "256m" diff --git a/subprojects/inline/src/test/java/org/mockitoinline/RecursionTest.java b/subprojects/inline/src/test/java/org/mockitoinline/RecursionTest.java new file mode 100644 index 0000000..2a4494e --- /dev/null +++ b/subprojects/inline/src/test/java/org/mockitoinline/RecursionTest.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2017 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitoinline; + +import org.junit.Test; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import static org.mockito.Mockito.spy; + +public class RecursionTest { + + @Test + public void testMockConcurrentHashMap() { + ConcurrentMap<String, String> map = spy(new ConcurrentHashMap<String, String>()); + map.putIfAbsent("a", "b"); + } +} diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StressTest.java b/subprojects/inline/src/test/java/org/mockitoinline/StressTest.java new file mode 100644 index 0000000..c6a5506 --- /dev/null +++ b/subprojects/inline/src/test/java/org/mockitoinline/StressTest.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitoinline; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +public class StressTest { + public class TestClass { + public String getStuff() { + return "A"; + } + } + + @Test + public void call_a_lot_of_mocks() { + //This requires smaller heap set for the test process, see "inline.gradle" + for (int i = 0; i < 40000; i++) { + TestClass mock = mock(TestClass.class); + when(mock.getStuff()).thenReturn("B"); + assertEquals("B", mock.getStuff()); + + TestClass serializableMock = mock(TestClass.class, withSettings().serializable()); + when(serializableMock.getStuff()).thenReturn("C"); + assertEquals("C", serializableMock.getStuff()); + + if (i % 1024 == 0) { + System.out.println(i + "/40000 mocks called"); + } + } + } +} diff --git a/subprojects/inline/src/test/java/org/mockitoinline/SuperCallTest.java b/subprojects/inline/src/test/java/org/mockitoinline/SuperCallTest.java new file mode 100644 index 0000000..1fe8196 --- /dev/null +++ b/subprojects/inline/src/test/java/org/mockitoinline/SuperCallTest.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitoinline; + +import org.junit.Test; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +public final class SuperCallTest { + + @Test + public void testSuperMethodCall() { + Dummy d = spy(new Dummy()); + d.foo(); + verify(d).bar(eq("baz")); + } + + static class Dummy { + + public void foo() { + bar("baz"); + } + + // Also fails if public. + void bar(String s) { + return; + } + } +} |