From 52e21482a9d45705c1a546dba45977ebae720285 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 23:17:56 +0100 Subject: [PATCH 01/14] Bump versions.bytebuddy from 1.14.1 to 1.14.2 (#2938) Bumps `versions.bytebuddy` from 1.14.1 to 1.14.2. Updates `net.bytebuddy:byte-buddy` from 1.14.1 to 1.14.2 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.1...byte-buddy-1.14.2) Updates `net.bytebuddy:byte-buddy-agent` from 1.14.1 to 1.14.2 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.1...byte-buddy-1.14.2) Updates `net.bytebuddy:byte-buddy-android` from 1.14.1 to 1.14.2 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.1...byte-buddy-1.14.2) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: net.bytebuddy:byte-buddy-agent dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: net.bytebuddy:byte-buddy-android dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index e661df164d..70456a0ada 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.14.1' +versions.bytebuddy = '1.14.2' versions.junitJupiter = '5.9.2' versions.errorprone = '2.18.0' From 7e6a9531a4080325451b2e7e68e018e1e4b40cfc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 23:18:15 +0100 Subject: [PATCH 02/14] Bump com.diffplug.spotless from 6.16.0 to 6.17.0 (#2940) Bumps com.diffplug.spotless from 6.16.0 to 6.17.0. --- updated-dependencies: - dependency-name: com.diffplug.spotless dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2d392fb667..160f8d7cc3 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.16.0' + id 'com.diffplug.spotless' version '6.17.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.46.0' id 'biz.aQute.bnd.builder' version '6.4.0' From f28b15d8755cd85f1a3cfc7d251f2d0db4644757 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 07:56:30 +0100 Subject: [PATCH 03/14] Bump org.codehaus.groovy:groovy from 3.0.15 to 3.0.16 (#2939) Bumps [org.codehaus.groovy:groovy](https://github.com/apache/groovy) from 3.0.15 to 3.0.16. - [Release notes](https://github.com/apache/groovy/releases) - [Commits](https://github.com/apache/groovy/commits) --- updated-dependencies: - dependency-name: org.codehaus.groovy:groovy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 70456a0ada..970b020249 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -32,7 +32,7 @@ libraries.osgi = 'org.osgi:osgi.core:8.0.0' libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.18.200' libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.4.0' -libraries.groovy = 'org.codehaus.groovy:groovy:3.0.15' +libraries.groovy = 'org.codehaus.groovy:groovy:3.0.16' def kotlinVersion = '1.8.10' libraries.kotlin = [ From b6861e3e10cf437ccd3ef5a8e3634e3e8cd9eef5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 20:17:44 +0100 Subject: [PATCH 04/14] Bump org.eclipse.platform:org.eclipse.osgi from 3.18.200 to 3.18.300 (#2941) Bumps [org.eclipse.platform:org.eclipse.osgi](https://github.com/eclipse-equinox/equinox) from 3.18.200 to 3.18.300. - [Release notes](https://github.com/eclipse-equinox/equinox/releases) - [Commits](https://github.com/eclipse-equinox/equinox/commits) --- updated-dependencies: - dependency-name: org.eclipse.platform:org.eclipse.osgi dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 970b020249..c8bb447c01 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -29,7 +29,7 @@ libraries.autoservice = "com.google.auto.service:auto-service:1.0.1" libraries.objenesis = 'org.objenesis:objenesis:3.3' libraries.osgi = 'org.osgi:osgi.core:8.0.0' -libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.18.200' +libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.18.300' libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.4.0' libraries.groovy = 'org.codehaus.groovy:groovy:3.0.16' From 393f0a16982ef3060d5db164bf51bfba8ce09a48 Mon Sep 17 00:00:00 2001 From: jfrantzius Date: Sat, 18 Mar 2023 20:28:50 +0100 Subject: [PATCH 05/14] Fail when InjectMocks has multiple candidate mocks to insert into field (#2942) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #2934 Co-authored-by: Jörg von Frantzius --- .../filter/TypeBasedCandidateFilter.java | 27 ++++++-- .../mockito/internal/exceptions/Reporter.java | 25 +++++++ .../GenericTypeMockMultipleMatchesTest.java | 69 +++++++++++++++++++ .../org/mockitousage/GenericTypeMockTest.java | 26 ------- 4 files changed, 115 insertions(+), 32 deletions(-) create mode 100644 subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockMultipleMatchesTest.java diff --git a/src/main/java/org/mockito/internal/configuration/injection/filter/TypeBasedCandidateFilter.java b/src/main/java/org/mockito/internal/configuration/injection/filter/TypeBasedCandidateFilter.java index 0c40478daa..343605f370 100644 --- a/src/main/java/org/mockito/internal/configuration/injection/filter/TypeBasedCandidateFilter.java +++ b/src/main/java/org/mockito/internal/configuration/injection/filter/TypeBasedCandidateFilter.java @@ -4,6 +4,8 @@ */ package org.mockito.internal.configuration.injection.filter; +import static org.mockito.internal.exceptions.Reporter.moreThanOneMockCandidate; + import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -136,11 +138,24 @@ public OngoingInjector filterCandidate( // being wrapped), and MockUtil.getMockSettings() throws exception for those } - return next.filterCandidate( - mockTypeMatches, - candidateFieldToBeInjected, - allRemainingCandidateFields, - injectee, - injectMocksField); + boolean wasMultipleMatches = mockTypeMatches.size() > 1; + + OngoingInjector result = + next.filterCandidate( + mockTypeMatches, + candidateFieldToBeInjected, + allRemainingCandidateFields, + injectee, + injectMocksField); + + if (wasMultipleMatches) { + // we had found multiple mocks matching by type, see whether following filters + // were able to reduce this to single match (e.g. by filtering for matching field names) + if (result == OngoingInjector.nop) { + // nope, following filters cannot reduce this to a single match + throw moreThanOneMockCandidate(candidateFieldToBeInjected, mocks); + } + } + return result; } } diff --git a/src/main/java/org/mockito/internal/exceptions/Reporter.java b/src/main/java/org/mockito/internal/exceptions/Reporter.java index 2101e72554..0609168465 100644 --- a/src/main/java/org/mockito/internal/exceptions/Reporter.java +++ b/src/main/java/org/mockito/internal/exceptions/Reporter.java @@ -14,6 +14,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; import org.mockito.exceptions.base.MockitoAssertionError; import org.mockito.exceptions.base.MockitoException; @@ -53,6 +54,7 @@ import org.mockito.invocation.Location; import org.mockito.invocation.MatchableInvocation; import org.mockito.listeners.InvocationListener; +import org.mockito.mock.MockName; import org.mockito.mock.SerializableMode; /** @@ -893,6 +895,29 @@ public static MockitoException cannotInjectDependency( details); } + public static MockitoException moreThanOneMockCandidate( + Field field, Collection mockCandidates) { + List mockNames = + mockCandidates.stream() + .map(MockUtil::getMockName) + .map(MockName::toString) + .collect(Collectors.toList()); + return new MockitoException( + join( + "Mockito couldn't inject mock dependency on field " + + "'" + + field + + "' that is annotated with @InjectMocks in your test, ", + "because there were multiple matching mocks (i.e. " + + "fields annotated with @Mock and having matching type): " + + String.join(", ", mockNames) + + ".", + "If you have multiple fields of same type in your class under test " + + "then consider naming the @Mock fields " + + "identically to the respective class under test's fields, " + + "so Mockito can match them by name.")); + } + private static String exceptionCauseMessageIfAvailable(Exception details) { if (details.getCause() == null) { return details.getMessage(); diff --git a/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockMultipleMatchesTest.java b/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockMultipleMatchesTest.java new file mode 100644 index 0000000000..323008eb63 --- /dev/null +++ b/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockMultipleMatchesTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 Mockito contributors + * This program is made available under the terms of the MIT License. + */ + +package org.mockitousage; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.exceptions.base.MockitoException; +import org.mockito.junit.jupiter.MockitoExtension; + +/** + * Verify that a {@link MockitoException} is thrown when there are multiple {@link Mock} fields that + * do match a candidate field by type, but cannot be matched by name. + * + * Uses a JUnit 5 extension to obtain the JUnit 5 {@link ExtensionContext} and + * pass it to {@link MockitoExtension#beforeEach(ExtensionContext)}, as the exception + * is thrown during {@link org.junit.jupiter.api.BeforeEach}. + */ +@ExtendWith(GenericTypeMockMultipleMatchesTest.ContextProvidingExtension.class) +public class GenericTypeMockMultipleMatchesTest { + + private static ExtensionContext currentExtensionContext; + + public static class ContextProvidingExtension implements BeforeEachCallback { + @Override + public void beforeEach(ExtensionContext context) throws Exception { + currentExtensionContext = context; + } + } + + private void startMocking(Object testInstance) { + MockitoExtension mockitoExtension = new MockitoExtension(); + mockitoExtension.beforeEach(currentExtensionContext); + } + + @Nested + public class MultipleCandidatesByTypeTest { + public class UnderTestWithMultipleCandidatesByType { + List stringList; + } + + @Mock + List stringList1; + + @Mock + List stringList2; + + @InjectMocks + UnderTestWithMultipleCandidatesByType underTestWithMultipleCandidates = new UnderTestWithMultipleCandidatesByType(); + + @Test + void testMultipleCandidatesByTypes() { + assertThrows(MockitoException.class, () -> startMocking(this)); + } + } + + +} diff --git a/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockTest.java b/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockTest.java index cfece7cb84..a837f0149b 100644 --- a/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockTest.java +++ b/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockTest.java @@ -33,7 +33,6 @@ @ExtendWith(MockitoExtension.class) public class GenericTypeMockTest { - @Nested public class SingleTypeParamTest { public class UnderTestWithSingleTypeParam { @@ -147,31 +146,6 @@ void testGenericSubclass() { } } - @Nested - public class MultipleCandidatesByTypeTest { - public class UnderTestWithMultipleCandidatesByType { - List stringList; - } - - @Mock - List stringList1; - - @Mock - List stringList2; - - @InjectMocks - UnderTestWithMultipleCandidatesByType underTestWithMultipleCandidates = new UnderTestWithMultipleCandidatesByType(); - - @Test - void testMultipleCandidatesByTypes() { - assertNotNull(stringList1); - assertNotNull(stringList2); - - // verify that when mutiple mock candidates exist with same type (but not matching by field names), none will be injected - assertNull(underTestWithMultipleCandidates.stringList); - } - } - @Nested public class MultipleCandidatesOneByNameTest { public class UnderTestWithMultipleCandidatesOneByName { From 568c82906f9f27f1c767729d99e6c08ebbcca571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Kraso=C5=84?= Date: Sat, 18 Mar 2023 20:50:58 +0100 Subject: [PATCH 06/14] Stop publishing `mockito-inline` (#2945) The tests will remain in the new `inlineTest` subproject. Fixes #2877 --- settings.gradle.kts | 2 +- subprojects/androidTest/androidTest.gradle | 2 +- .../groovyInlineTest/groovyInlineTest.gradle | 2 +- .../org.mockito.plugins.MemberAccessor | 1 - .../org.mockito.plugins.MockMaker | 1 - .../inlineTest.gradle} | 17 +++++++++++------ .../mockitoinline/ConstructionMockRuleTest.java | 0 .../org/mockitoinline/ConstructionMockTest.java | 0 .../java/org/mockitoinline/EnumMockingTest.java | 0 .../mockitoinline/FinalClassMockingTest.java | 0 .../HierarchyPreInitializationTest.java | 0 .../mockitoinline/InOrderVerificationTest.java | 0 .../org/mockitoinline/InitializationTest.java | 0 .../mockitoinline/OneLinerStubStressTest.java | 0 .../test/java/org/mockitoinline/PluginTest.java | 0 .../java/org/mockitoinline/RecursionTest.java | 0 .../mockitoinline/SpyWithConstructorTest.java | 0 .../org/mockitoinline/StaticMockRuleTest.java | 0 .../java/org/mockitoinline/StaticMockTest.java | 0 .../java/org/mockitoinline/StaticRuleTest.java | 0 .../org/mockitoinline/StaticRunnerTest.java | 0 .../test/java/org/mockitoinline/StressTest.java | 0 .../org/mockitoinline/StubbingLocationTest.java | 0 .../mockitoinline/SubconstructorMockTest.java | 0 .../java/org/mockitoinline/SuperCallTest.java | 0 .../CyclicMockMethodArgumentMemoryLeakTest.java | 0 .../bugs/OngoingStubShiftTest.java | 0 .../bugs/SelfSpyReferenceMemoryLeakTest.java | 0 28 files changed, 14 insertions(+), 11 deletions(-) delete mode 100644 subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor delete mode 100644 subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker rename subprojects/{inline/inline.gradle => inlineTest/inlineTest.gradle} (54%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/ConstructionMockRuleTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/ConstructionMockTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/EnumMockingTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/FinalClassMockingTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/HierarchyPreInitializationTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/InOrderVerificationTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/InitializationTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/OneLinerStubStressTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/PluginTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/RecursionTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/SpyWithConstructorTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/StaticMockRuleTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/StaticMockTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/StaticRuleTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/StaticRunnerTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/StressTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/StubbingLocationTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/SubconstructorMockTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/SuperCallTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/bugs/CyclicMockMethodArgumentMemoryLeakTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java (100%) rename subprojects/{inline => inlineTest}/src/test/java/org/mockitoinline/bugs/SelfSpyReferenceMemoryLeakTest.java (100%) diff --git a/settings.gradle.kts b/settings.gradle.kts index 5806de0315..a0a66b7764 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,7 +3,7 @@ plugins { } include("subclass", - "inline", + "inlineTest", "proxy", "extTest", "groovyTest", diff --git a/subprojects/androidTest/androidTest.gradle b/subprojects/androidTest/androidTest.gradle index 941a52b9cc..072c222639 100644 --- a/subprojects/androidTest/androidTest.gradle +++ b/subprojects/androidTest/androidTest.gradle @@ -38,7 +38,7 @@ apply from: "$rootDir/gradle/dependencies.gradle" dependencies { implementation libraries.kotlin.stdlib - testImplementation project(":inline") + testImplementation project(":") testImplementation libraries.junit4 testImplementation libraries.junitJupiterApi testImplementation libraries.junitJupiterEngine diff --git a/subprojects/groovyInlineTest/groovyInlineTest.gradle b/subprojects/groovyInlineTest/groovyInlineTest.gradle index 1618ac6009..16e95a9118 100644 --- a/subprojects/groovyInlineTest/groovyInlineTest.gradle +++ b/subprojects/groovyInlineTest/groovyInlineTest.gradle @@ -5,7 +5,7 @@ description = "Integration test for using mockito-inline with Groovy." apply from: "$rootDir/gradle/dependencies.gradle" dependencies { - testImplementation project(":inline") + testImplementation project(":") testImplementation libraries.groovy testImplementation libraries.junit4 } diff --git a/subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor b/subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor deleted file mode 100644 index 1422f9900b..0000000000 --- a/subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MemberAccessor +++ /dev/null @@ -1 +0,0 @@ -member-accessor-module diff --git a/subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker b/subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker deleted file mode 100644 index ca6ee9cea8..0000000000 --- a/subprojects/inline/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker +++ /dev/null @@ -1 +0,0 @@ -mock-maker-inline \ No newline at end of file diff --git a/subprojects/inline/inline.gradle b/subprojects/inlineTest/inlineTest.gradle similarity index 54% rename from subprojects/inline/inline.gradle rename to subprojects/inlineTest/inlineTest.gradle index ef421f0c02..4cc6726c5a 100644 --- a/subprojects/inline/inline.gradle +++ b/subprojects/inlineTest/inlineTest.gradle @@ -1,19 +1,24 @@ +plugins { + id 'java' +} + description = "Mockito preconfigured inline mock maker (intermediate and to be superseeded by automatic usage in a future version)" -apply from: "$rootDir/gradle/java-library.gradle" +apply from: "$rootDir/gradle/dependencies.gradle" + +sourceCompatibility = 11 +targetCompatibility = 11 dependencies { - api project.rootProject + implementation project.rootProject testImplementation libraries.junit4 testImplementation libraries.assertj } tasks.javadoc.enabled = false -//required by the "StressTest.java" and "OneLinerStubStressTest.java" -test.maxHeapSize = "256m" -retryTest.maxHeapSize = "256m" - test { jvmArgs '--illegal-access=deny' + //required by the "StressTest.java" and "OneLinerStubStressTest.java" + maxHeapSize '256m' } diff --git a/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockRuleTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/ConstructionMockRuleTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockRuleTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/ConstructionMockRuleTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/ConstructionMockTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/ConstructionMockTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/EnumMockingTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/EnumMockingTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/EnumMockingTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/EnumMockingTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/FinalClassMockingTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/FinalClassMockingTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/FinalClassMockingTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/FinalClassMockingTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/HierarchyPreInitializationTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/HierarchyPreInitializationTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/HierarchyPreInitializationTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/HierarchyPreInitializationTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/InOrderVerificationTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/InOrderVerificationTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/InOrderVerificationTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/InitializationTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/InitializationTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/InitializationTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/InitializationTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/OneLinerStubStressTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/OneLinerStubStressTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/OneLinerStubStressTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/OneLinerStubStressTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/PluginTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/PluginTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/PluginTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/PluginTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/RecursionTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/RecursionTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/RecursionTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/RecursionTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/SpyWithConstructorTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/SpyWithConstructorTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/SpyWithConstructorTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/SpyWithConstructorTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StaticMockRuleTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/StaticMockRuleTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/StaticMockRuleTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/StaticMockRuleTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StaticMockTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/StaticMockTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/StaticMockTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/StaticMockTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StaticRuleTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/StaticRuleTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/StaticRuleTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/StaticRuleTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StaticRunnerTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/StaticRunnerTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/StaticRunnerTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/StaticRunnerTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StressTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/StressTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/StressTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/StressTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/StubbingLocationTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/StubbingLocationTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/StubbingLocationTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/StubbingLocationTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/SubconstructorMockTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/SubconstructorMockTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/SubconstructorMockTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/SubconstructorMockTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/SuperCallTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/SuperCallTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/SuperCallTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/SuperCallTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/bugs/CyclicMockMethodArgumentMemoryLeakTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/bugs/CyclicMockMethodArgumentMemoryLeakTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/bugs/CyclicMockMethodArgumentMemoryLeakTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/bugs/CyclicMockMethodArgumentMemoryLeakTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/bugs/OngoingStubShiftTest.java diff --git a/subprojects/inline/src/test/java/org/mockitoinline/bugs/SelfSpyReferenceMemoryLeakTest.java b/subprojects/inlineTest/src/test/java/org/mockitoinline/bugs/SelfSpyReferenceMemoryLeakTest.java similarity index 100% rename from subprojects/inline/src/test/java/org/mockitoinline/bugs/SelfSpyReferenceMemoryLeakTest.java rename to subprojects/inlineTest/src/test/java/org/mockitoinline/bugs/SelfSpyReferenceMemoryLeakTest.java From 5486617f7ac759d24e99ee9d8d03e969acdd460f Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Sat, 25 Mar 2023 13:30:31 +0100 Subject: [PATCH 07/14] Correct visibility check to respect nestmates (#2948) For subclasses which can invoke private constructors if they are nested classes Fixes #2947 --- .../creation/bytebuddy/MockMethodAdvice.java | 4 +-- .../InlineDelegateByteBuddyMockMakerTest.java | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) 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 6074eef038..2c3649ea0e 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockMethodAdvice.java @@ -4,8 +4,8 @@ */ package org.mockito.internal.creation.bytebuddy; +import static net.bytebuddy.matcher.ElementMatchers.isAccessibleTo; import static net.bytebuddy.matcher.ElementMatchers.isConstructor; -import static net.bytebuddy.matcher.ElementMatchers.isPrivate; import static net.bytebuddy.matcher.ElementMatchers.isStatic; import static net.bytebuddy.matcher.ElementMatchers.not; @@ -398,7 +398,7 @@ public MethodVisitor wrap( .getSuperClass() .asErasure() .getDeclaredMethods() - .filter(isConstructor().and(not(isPrivate()))); + .filter(isConstructor().and(isAccessibleTo(instrumentedType))); int arguments = Integer.MAX_VALUE; boolean packagePrivate = true; MethodDescription.InDefinedShape current = null; diff --git a/src/test/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMakerTest.java b/src/test/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMakerTest.java index 3d42f3a377..89aa415a96 100644 --- a/src/test/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMakerTest.java +++ b/src/test/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMakerTest.java @@ -80,6 +80,22 @@ public void should_create_mock_from_final_spy() throws Exception { }); } + @Test + public void should_create_mock_from_accessible_inner_spy() throws Exception { + MockCreationSettings settings = settingsFor(Outer.Inner.class); + Optional proxy = + mockMaker.createSpy( + settings, + new MockHandlerImpl<>(settings), + new Outer.Inner(new Object(), new Object())); + assertThat(proxy) + .hasValueSatisfying( + spy -> { + assertThat(spy.p1).isNotNull(); + assertThat(spy.p2).isNotNull(); + }); + } + @Test public void should_create_mock_from_non_constructable_class() throws Exception { MockCreationSettings settings = @@ -646,4 +662,23 @@ void internalThrowException(int test) throws IOException { } } } + + static class Outer { + + final Object p1; + + private Outer(final Object p1) { + this.p1 = p1; + } + + private static class Inner extends Outer { + + final Object p2; + + Inner(final Object p1, final Object p2) { + super(p1); + this.p2 = p2; + } + } + } } From 0b8685c37be46d43b80638d9e9e5ba9efd35f291 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Mar 2023 20:25:42 +0200 Subject: [PATCH 08/14] Bump versions.bytebuddy from 1.14.2 to 1.14.3 (#2950) Bumps `versions.bytebuddy` from 1.14.2 to 1.14.3. Updates `net.bytebuddy:byte-buddy` from 1.14.2 to 1.14.3 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.2...byte-buddy-1.14.3) Updates `net.bytebuddy:byte-buddy-agent` from 1.14.2 to 1.14.3 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.2...byte-buddy-1.14.3) Updates `net.bytebuddy:byte-buddy-android` from 1.14.2 to 1.14.3 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.2...byte-buddy-1.14.3) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: net.bytebuddy:byte-buddy-agent dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: net.bytebuddy:byte-buddy-android dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index c8bb447c01..1f2044ea02 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.14.2' +versions.bytebuddy = '1.14.3' versions.junitJupiter = '5.9.2' versions.errorprone = '2.18.0' From f9fbf50bd63b71d1b4ec86426a02b3bcb6cdd3f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Mar 2023 21:53:29 +0200 Subject: [PATCH 09/14] Bump org.codehaus.groovy:groovy from 3.0.16 to 3.0.17 (#2959) Bumps [org.codehaus.groovy:groovy](https://github.com/apache/groovy) from 3.0.16 to 3.0.17. - [Release notes](https://github.com/apache/groovy/releases) - [Commits](https://github.com/apache/groovy/commits) --- updated-dependencies: - dependency-name: org.codehaus.groovy:groovy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 1f2044ea02..defc16d24d 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -32,7 +32,7 @@ libraries.osgi = 'org.osgi:osgi.core:8.0.0' libraries.equinox = 'org.eclipse.platform:org.eclipse.osgi:3.18.300' libraries.bndGradle = 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.4.0' -libraries.groovy = 'org.codehaus.groovy:groovy:3.0.16' +libraries.groovy = 'org.codehaus.groovy:groovy:3.0.17' def kotlinVersion = '1.8.10' libraries.kotlin = [ From 600eaa06f4a1b337532497c80a301ec689b4ddc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 9 Apr 2023 15:39:48 +0200 Subject: [PATCH 10/14] Bump com.diffplug.spotless from 6.17.0 to 6.18.0 (#2967) Bumps com.diffplug.spotless from 6.17.0 to 6.18.0. --- updated-dependencies: - dependency-name: com.diffplug.spotless dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 160f8d7cc3..de764459c5 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { } plugins { - id 'com.diffplug.spotless' version '6.17.0' + id 'com.diffplug.spotless' version '6.18.0' id 'eclipse' id 'com.github.ben-manes.versions' version '0.46.0' id 'biz.aQute.bnd.builder' version '6.4.0' From d65bba7e6a40a3bbd51dd7272dc5bc8bb5c2199f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 9 Apr 2023 15:40:42 +0200 Subject: [PATCH 11/14] Bump versions.bytebuddy from 1.14.3 to 1.14.4 (#2966) Bumps `versions.bytebuddy` from 1.14.3 to 1.14.4. Updates `net.bytebuddy:byte-buddy` from 1.14.3 to 1.14.4 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.3...byte-buddy-1.14.4) Updates `net.bytebuddy:byte-buddy-agent` from 1.14.3 to 1.14.4 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.3...byte-buddy-1.14.4) Updates `net.bytebuddy:byte-buddy-android` from 1.14.3 to 1.14.4 - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.3...byte-buddy-1.14.4) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: net.bytebuddy:byte-buddy-agent dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: net.bytebuddy:byte-buddy-android dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index defc16d24d..d97e022c15 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -4,7 +4,7 @@ ext { def versions = [:] -versions.bytebuddy = '1.14.3' +versions.bytebuddy = '1.14.4' versions.junitJupiter = '5.9.2' versions.errorprone = '2.18.0' From 84a5eee1ce5bb0171532eb5da3d21ba9d1741bf9 Mon Sep 17 00:00:00 2001 From: jfrantzius Date: Sun, 9 Apr 2023 15:46:39 +0200 Subject: [PATCH 12/14] Fix ClassCastException regression with parameterized types with InjectMocks (#2962) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #2958 Co-authored-by: Jörg von Frantzius --- .../filter/TypeBasedCandidateFilter.java | 126 +++++++++++------- .../org/mockitousage/GenericTypeMockTest.java | 86 ++++++++++++ 2 files changed, 166 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/mockito/internal/configuration/injection/filter/TypeBasedCandidateFilter.java b/src/main/java/org/mockito/internal/configuration/injection/filter/TypeBasedCandidateFilter.java index 343605f370..a66ab52144 100644 --- a/src/main/java/org/mockito/internal/configuration/injection/filter/TypeBasedCandidateFilter.java +++ b/src/main/java/org/mockito/internal/configuration/injection/filter/TypeBasedCandidateFilter.java @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.stream.Stream; import org.mockito.internal.util.MockUtil; @@ -28,26 +29,38 @@ public TypeBasedCandidateFilter(MockCandidateFilter next) { protected boolean isCompatibleTypes(Type typeToMock, Type mockType, Field injectMocksField) { boolean result = false; - if (typeToMock instanceof ParameterizedType && mockType instanceof ParameterizedType) { - // ParameterizedType.equals() is documented as: - // "Instances of classes that implement this interface must implement - // an equals() method that equates any two instances that share the - // same generic type declaration and have equal type parameters." - // Unfortunately, e.g. Wildcard parameter "?" doesn't equal java.lang.String, - // and e.g. Set doesn't equal TreeSet, so roll our own comparison if - // ParameterizedTypeImpl.equals() returns false - if (typeToMock.equals(mockType)) { - result = true; + if (typeToMock instanceof ParameterizedType) { + if (mockType instanceof ParameterizedType) { + // ParameterizedType.equals() is documented as: + // "Instances of classes that implement this interface must implement + // an equals() method that equates any two instances that share the + // same generic type declaration and have equal type parameters." + // Unfortunately, e.g. Wildcard parameter "?" doesn't equal java.lang.String, + // and e.g. Set doesn't equal TreeSet, so roll our own comparison if + // ParameterizedTypeImpl.equals() returns false + if (typeToMock.equals(mockType)) { + result = true; + } else { + ParameterizedType genericTypeToMock = (ParameterizedType) typeToMock; + ParameterizedType genericMockType = (ParameterizedType) mockType; + Type[] actualTypeArguments = genericTypeToMock.getActualTypeArguments(); + Type[] actualTypeArguments2 = genericMockType.getActualTypeArguments(); + // Recurse on type parameters, so we properly test whether e.g. Wildcard bounds + // have a match + result = + recurseOnTypeArguments( + injectMocksField, actualTypeArguments, actualTypeArguments2); + } } else { - ParameterizedType genericTypeToMock = (ParameterizedType) typeToMock; - ParameterizedType genericMockType = (ParameterizedType) mockType; - Type[] actualTypeArguments = genericTypeToMock.getActualTypeArguments(); - Type[] actualTypeArguments2 = genericMockType.getActualTypeArguments(); - // Recurse on type parameters, so we properly test whether e.g. Wildcard bounds - // have a match + // mockType is a non-parameterized Class, i.e. a concrete class. + // so walk concrete class' type hierarchy + Class concreteMockClass = (Class) mockType; + Stream mockSuperTypes = getSuperTypes(concreteMockClass); result = - recurseOnTypeArguments( - injectMocksField, actualTypeArguments, actualTypeArguments2); + mockSuperTypes.anyMatch( + mockSuperType -> + isCompatibleTypes( + typeToMock, mockSuperType, injectMocksField)); } } else if (typeToMock instanceof WildcardType) { WildcardType wildcardTypeToMock = (WildcardType) typeToMock; @@ -56,12 +69,19 @@ protected boolean isCompatibleTypes(Type typeToMock, Type mockType, Field inject Arrays.stream(upperBounds) .anyMatch(t -> isCompatibleTypes(t, mockType, injectMocksField)); } else if (typeToMock instanceof Class && mockType instanceof Class) { - result = ((Class) typeToMock).isAssignableFrom((Class) mockType); + result = ((Class) typeToMock).isAssignableFrom((Class) mockType); } // no need to check for GenericArrayType, as Mockito cannot mock this anyway return result; } + private Stream getSuperTypes(Class concreteMockClass) { + Stream mockInterfaces = Arrays.stream(concreteMockClass.getGenericInterfaces()); + Stream mockSuperTypes = + Stream.concat(mockInterfaces, Stream.of(concreteMockClass.getGenericSuperclass())); + return mockSuperTypes; + } + private boolean recurseOnTypeArguments( Field injectMocksField, Type[] actualTypeArguments, Type[] actualTypeArguments2) { boolean isCompatible = true; @@ -76,30 +96,44 @@ private boolean recurseOnTypeArguments( // The TypeVariable`s actual type is declared by the field containing // the object under test, i.e. the field annotated with @InjectMocks // e.g. @InjectMocks ClassUnderTest underTest = .. - Type[] injectMocksFieldTypeParameters = - ((ParameterizedType) injectMocksField.getGenericType()) - .getActualTypeArguments(); - // Find index of given TypeVariable where it was defined, e.g. 0 for T1 in - // ClassUnderTest - // (we're always able to find it, otherwise test class wouldn't have compiled)) - TypeVariable[] genericTypeParameters = - injectMocksField.getType().getTypeParameters(); - int variableIndex = -1; - for (int i2 = 0; i2 < genericTypeParameters.length; i2++) { - if (genericTypeParameters[i2].equals(typeVariable)) { - variableIndex = i2; - break; + + Type genericType = injectMocksField.getGenericType(); + if (genericType instanceof ParameterizedType) { + Type[] injectMocksFieldTypeParameters = + ((ParameterizedType) genericType).getActualTypeArguments(); + // Find index of given TypeVariable where it was defined, e.g. 0 for T1 in + // ClassUnderTest + // (we're always able to find it, otherwise test class wouldn't have compiled)) + TypeVariable[] genericTypeParameters = + injectMocksField.getType().getTypeParameters(); + int variableIndex = -1; + for (int i2 = 0; i2 < genericTypeParameters.length; i2++) { + if (genericTypeParameters[i2].equals(typeVariable)) { + variableIndex = i2; + break; + } } + // now test whether actual type for the type variable is compatible, e.g. for + // class ClassUnderTest {..} + // T1 would be the String in + // ClassUnderTest underTest = .. + isCompatible &= + isCompatibleTypes( + injectMocksFieldTypeParameters[variableIndex], + actualTypeArgument2, + injectMocksField); + } else { + // must be a concrete class, recurse on super types that may have type + // parameters + isCompatible &= + getSuperTypes((Class) genericType) + .anyMatch( + superType -> + isCompatibleTypes( + superType, + actualTypeArgument2, + injectMocksField)); } - // now test whether actual type for the type variable is compatible, e.g. for - // class ClassUnderTest {..} - // T1 would be the String in - // ClassUnderTest underTest = .. - isCompatible &= - isCompatibleTypes( - injectMocksFieldTypeParameters[variableIndex], - actualTypeArgument2, - injectMocksField); } else { isCompatible &= isCompatibleTypes( @@ -119,12 +153,12 @@ public OngoingInjector filterCandidate( List mockTypeMatches = new ArrayList<>(); for (Object mock : mocks) { if (candidateFieldToBeInjected.getType().isAssignableFrom(mock.getClass())) { - Type genericMockType = MockUtil.getMockSettings(mock).getGenericTypeToMock(); - Type genericType = candidateFieldToBeInjected.getGenericType(); - boolean bothHaveGenericTypeInfo = genericType != null && genericMockType != null; - if (bothHaveGenericTypeInfo) { + Type mockType = MockUtil.getMockSettings(mock).getGenericTypeToMock(); + Type typeToMock = candidateFieldToBeInjected.getGenericType(); + boolean bothHaveTypeInfo = typeToMock != null && mockType != null; + if (bothHaveTypeInfo) { // be more specific if generic type information is available - if (isCompatibleTypes(genericType, genericMockType, injectMocksField)) { + if (isCompatibleTypes(typeToMock, mockType, injectMocksField)) { mockTypeMatches.add(mock); } // else filter out mock, as generic types don't match } else { diff --git a/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockTest.java b/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockTest.java index a837f0149b..4decb2e2e2 100644 --- a/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockTest.java +++ b/subprojects/junit-jupiter/src/test/java/org/mockitousage/GenericTypeMockTest.java @@ -8,8 +8,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.MockitoAnnotations.*; import java.sql.Time; +import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashSet; @@ -18,6 +20,7 @@ import java.util.Set; import java.util.TreeSet; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -260,4 +263,87 @@ void testWithTypeParameters() { } } + @Nested + public class InjectConcreteClassInFieldWithTypeParameter { + public class UnderTestWithTypeParameter { + List tList; + } + + public class ConcreteStringList extends ArrayList {} + + @Mock + ConcreteStringList concreteStringList; + + @InjectMocks + UnderTestWithTypeParameter underTestWithTypeParameters = new UnderTestWithTypeParameter(); + + @Test + void testWithTypeParameters() { + assertNotNull(concreteStringList); + + // verify that we can match the type parameters of the class under test + assertEquals(concreteStringList, underTestWithTypeParameters.tList); + } + } + + @Nested + public class NoneMatchInjectConcreteClassInFieldWithTypeParameterTest { + public class UnderTestWithTypeParameter { + List tList; + } + + public class ConcreteStringList extends ArrayList {} + + @Mock + ConcreteStringList concreteStringList; + + @InjectMocks + UnderTestWithTypeParameter underTestWithTypeParameters = new UnderTestWithTypeParameter(); + + @Test + void testWithTypeParameters() { + assertNotNull(concreteStringList); + + // verify that when no concrete type candidate matches, none is injected + assertNull(underTestWithTypeParameters.tList); + } + } + + /** + * Verify regression https://github.com/mockito/mockito/issues/2958 is fixed. + */ + @Nested + public class RegressionClassCastException { + public class AbstractUnderTest> { + UnderTestInstance instance; + } + + public class UnderTestInstance> { + } + + public class ConcreteUnderTest extends AbstractUnderTest { + } + + @Mock + UnderTestInstance instanceMock; + + @InjectMocks + protected ConcreteUnderTest concreteUnderTest = new ConcreteUnderTest(); + + @BeforeEach + public void initMocks() + { + openMocks(this); + } + + @Test + public void testMockExists() { + assertNotNull(instanceMock); + assertEquals(instanceMock, concreteUnderTest.instance); + } + + + } + } + From 203132db493cda6c8269fb14812b1b81e6f042a9 Mon Sep 17 00:00:00 2001 From: Wesley Tsai Date: Tue, 11 Apr 2023 11:56:30 -0700 Subject: [PATCH 13/14] Add `withoutAnnotations` parameter to `@Mock` (#2965) Co-authored-by: Wesley Tsai --- src/main/java/org/mockito/Mock.java | 7 +++++++ .../internal/configuration/MockAnnotationProcessor.java | 3 +++ .../java/org/mockitousage/annotation/AnnotationsTest.java | 8 ++++++++ 3 files changed, 18 insertions(+) diff --git a/src/main/java/org/mockito/Mock.java b/src/main/java/org/mockito/Mock.java index 2e4f515035..4d94edfe26 100644 --- a/src/main/java/org/mockito/Mock.java +++ b/src/main/java/org/mockito/Mock.java @@ -132,6 +132,13 @@ */ String mockMaker() default ""; + /** + * Mock will not attempt to preserve all annotation metadata, see {@link MockSettings#withoutAnnotations()}. + * + * @since 5.3.0 + */ + boolean withoutAnnotations() default false; + enum Strictness { /** diff --git a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java index fed851dbe7..f61f4a7bd9 100644 --- a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java +++ b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java @@ -56,6 +56,9 @@ public static Object processAnnotationForMock( if (!annotation.mockMaker().isEmpty()) { mockSettings.mockMaker(annotation.mockMaker()); } + if (annotation.withoutAnnotations()) { + mockSettings.withoutAnnotations(); + } mockSettings.genericTypeToMock(genericType.get()); diff --git a/src/test/java/org/mockitousage/annotation/AnnotationsTest.java b/src/test/java/org/mockitousage/annotation/AnnotationsTest.java index 1a33f933b2..939888a73d 100644 --- a/src/test/java/org/mockitousage/annotation/AnnotationsTest.java +++ b/src/test/java/org/mockitousage/annotation/AnnotationsTest.java @@ -88,6 +88,9 @@ public void shouldLookForAnnotatedMocksInSuperClasses() throws Exception { @Mock(stubOnly = true) IMethods stubOnly; + @Mock(withoutAnnotations = true) + IMethods withoutAnnotations; + @Test public void shouldInitMocksWithGivenSettings() throws Exception { assertEquals("i have a name", namedAndReturningMocks.toString()); @@ -99,6 +102,11 @@ public void shouldInitMocksWithGivenSettings() throws Exception { assertTrue(hasExtraInterfaces instanceof List); assertTrue(Mockito.mockingDetails(stubOnly).getMockCreationSettings().isStubOnly()); + assertTrue( + Mockito.mockingDetails(withoutAnnotations) + .getMockCreationSettings() + .isStripAnnotations()); + assertEquals(0, noExtraConfig.intReturningMethod()); } From 6ffd23e1bf3866dc39ab13c86ebe1f667e07398d Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Tue, 11 Apr 2023 20:58:39 +0200 Subject: [PATCH 14/14] Add `ArgumentMatchers#assertArg` method (#2949) Fixes #2285 --- .../java/org/mockito/ArgumentMatchers.java | 17 +++++++ src/main/java/org/mockito/Mockito.java | 14 ++++++ .../mockitousage/matchers/MatchersTest.java | 49 +++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/src/main/java/org/mockito/ArgumentMatchers.java b/src/main/java/org/mockito/ArgumentMatchers.java index de87f298a1..1743ce164b 100644 --- a/src/main/java/org/mockito/ArgumentMatchers.java +++ b/src/main/java/org/mockito/ArgumentMatchers.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import java.util.regex.Pattern; import org.mockito.internal.matchers.Any; @@ -912,6 +913,22 @@ public static T argThat(ArgumentMatcher matcher) { return null; } + /** + * Allows creating custom argument matchers where matching is considered successful when the consumer given by parameter does not throw an exception. + *

+ * Typically used with {@link Mockito#verify(Object)} to execute assertions on parameters passed to the verified method invocation. + * + * @param consumer executes assertions on the verified argument + * @return null. + */ + public static T assertArg(Consumer consumer) { + return argThat( + argument -> { + consumer.accept(argument); + return true; + }); + } + /** * Allows creating custom char argument matchers. *

diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 428655e4a8..1fce63e51e 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -39,6 +39,7 @@ import org.mockito.verification.VerificationMode; import org.mockito.verification.VerificationWithTimeout; +import java.util.function.Consumer; import java.util.function.Function; /** @@ -1661,6 +1662,19 @@ * With an implicit type, the Java compiler is unable to automatically determine the type of a mock and you need * to pass in the {@code Class} explicitly. *

+ * + *
+ * + * To validate arguments during verification, instead of capturing them with {@link ArgumentCaptor}, you can now + * use {@link ArgumentMatchers#assertArg(Consumer)}}: + * + *

+ *   verify(serviceMock).doStuff(assertArg(param -> {
+ *     assertThat(param.getField1()).isEqualTo("foo");
+ *     assertThat(param.getField2()).isEqualTo("bar");
+ *   }));
+ * 
*/ @CheckReturnValue @SuppressWarnings("unchecked") diff --git a/src/test/java/org/mockitousage/matchers/MatchersTest.java b/src/test/java/org/mockitousage/matchers/MatchersTest.java index 777c49a063..f89b44b731 100644 --- a/src/test/java/org/mockitousage/matchers/MatchersTest.java +++ b/src/test/java/org/mockitousage/matchers/MatchersTest.java @@ -20,6 +20,7 @@ import static org.mockito.AdditionalMatchers.lt; import static org.mockito.AdditionalMatchers.not; import static org.mockito.AdditionalMatchers.or; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.any; @@ -52,6 +53,7 @@ import java.util.RandomAccess; import java.util.regex.Pattern; +import org.junit.ComparisonFailure; import org.junit.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; @@ -624,4 +626,51 @@ public void nullable_matcher() throws Exception { verify(mock, times(2)).oneArg(nullable(Character.class)); } + + @Test + public void assertArg_matcher() throws Exception { + mock.oneArg("hello"); + + verify(mock).oneArg(assertArg((String it) -> assertEquals("hello", it))); + } + + @Test + public void assertArg_matcher_fails_when_assertion_fails() throws Exception { + mock.oneArg("hello"); + + try { + verify(mock).oneArg(assertArg((String it) -> assertEquals("not-hello", it))); + fail("Should throw an exception"); + } catch (ComparisonFailure e) { + // do nothing + } + } + + @Test + public void can_invoke_method_on_mock_after_assert_arg() throws Exception { + mock.oneArg("hello"); + + try { + verify(mock).oneArg(assertArg((String it) -> assertEquals("not-hello", it))); + fail("Should throw an exception"); + } catch (ComparisonFailure e) { + // do nothing + } + + mock.oneArg("hello"); + } + + @Test + public void can_verify_on_mock_after_assert_arg() throws Exception { + mock.oneArg("hello"); + + try { + verify(mock).oneArg(assertArg((String it) -> assertEquals("not-hello", it))); + fail("Should throw an exception"); + } catch (ComparisonFailure e) { + // do nothing + } + + verify(mock).oneArg("hello"); + } }

55. + * Verification with assertions (Since 5.3.0)