-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Improves non mockability reasons also report mock maker, mockito version #3535
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ab1ef91
1cba555
3fb4378
bb04091
52e746c
b59e49f
5eaf285
d3b0964
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -74,6 +74,7 @@ jobs: | |
| ./gradlew | ||
| -Pmockito.test.java=${{ matrix.java }} | ||
| build | ||
| --no-daemon | ||
| --stacktrace | ||
| --scan | ||
| env: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import com.android.build.gradle.internal.tasks.factory.dependsOn | ||
| import com.diffplug.gradle.spotless.SpotlessExtension | ||
|
|
||
| plugins { | ||
| id("mockito.library-conventions") | ||
|
|
@@ -200,3 +201,53 @@ fun mockitoExtensionConfigFile(project: Project, mockitoExtension: String) = | |
| file(project.layout.buildDirectory.dir("generated/resources/ext-config/test/mockito-extensions/$mockitoExtension")) | ||
| //</editor-fold> | ||
|
|
||
| //<editor-fold defaultstate="collapsed" desc="Generate version class file"> | ||
| // Manifest cannot be used because Android strips the manifest from the jar. | ||
|
|
||
| val generateVersionClass by tasks.registering(GenerateVersionClass::class) | ||
|
|
||
| sourceSets.main { | ||
| java { | ||
| srcDir(generateVersionClass) | ||
| } | ||
| } | ||
|
|
||
| tasks.withType<Checkstyle> { | ||
| exclude("org/mockito/internal/util/MockitoVersion.java") | ||
| } | ||
|
|
||
| configure<SpotlessExtension> { | ||
| java { | ||
| targetExclude(relativePath(generateVersionClass.get().generatedVersionClassDir) + "/**") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: to avoid eager configuration, let's also define a constant for val generatedVersionClassDirLocation = project.layout.buildDirectory.dir("generated/sources/version/java")
generateVersionClass.configure {
generatedVersionClassDir(generatedVersionClassDirLocation)
}
configure<SpotlessExtension> {
java {
targetExclude(relativePath(generatedVersionClassDirLocation) + "/**")
}
}That also allows us to remove the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good observation! |
||
| } | ||
| } | ||
|
|
||
| abstract class GenerateVersionClass : DefaultTask() { | ||
| @OutputDirectory | ||
| val generatedVersionClassDir: DirectoryProperty = project.objects.directoryProperty() | ||
| .convention(project.layout.buildDirectory.dir("generated/sources/version/java")) | ||
|
|
||
| @Input | ||
| val version: Property<String> = project.objects.property(String::class.java) | ||
| .convention(project.version.toString()) | ||
|
|
||
| @TaskAction | ||
| fun generate() { | ||
| generatedVersionClassDir.file("org/mockito/internal/util/MockitoVersion.java").get() | ||
| .asFile | ||
| .run { | ||
| parentFile.mkdirs() | ||
| createNewFile() | ||
| writeText( | ||
| // language=Java | ||
| """ | ||
| package org.mockito.internal.util; | ||
| class MockitoVersion { | ||
| public static final String VERSION = "${version.get()}"; | ||
| } | ||
| """.trimIndent() | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| //</editor-fold> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| */ | ||
| package org.mockito.internal.creation.bytebuddy; | ||
|
|
||
| import org.mockito.MockMakers; | ||
| import org.mockito.MockedConstruction; | ||
| import org.mockito.creation.instance.InstantiationException; | ||
| import org.mockito.creation.instance.Instantiator; | ||
|
|
@@ -15,6 +16,7 @@ | |
| import org.mockito.internal.configuration.plugins.Plugins; | ||
| import org.mockito.internal.creation.instance.ConstructorInstantiator; | ||
| import org.mockito.internal.framework.DisabledMockHandler; | ||
| import org.mockito.internal.util.MockitoInformation; | ||
| import org.mockito.internal.util.Platform; | ||
| import org.mockito.internal.util.concurrent.DetachedThreadLocal; | ||
| import org.mockito.internal.util.concurrent.WeakConcurrentMap; | ||
|
|
@@ -253,7 +255,8 @@ class InlineDelegateByteBuddyMockMaker | |
| "Could not initialize inline Byte Buddy mock maker.", | ||
| "", | ||
| detail, | ||
| Platform.describe()), | ||
| Platform.describe(), | ||
| MockitoInformation.describe(MockMakers.INLINE)), | ||
| INITIALIZATION_ERROR); | ||
| } | ||
|
|
||
|
|
@@ -436,23 +439,6 @@ private <T> RuntimeException prettifyFailure( | |
| "Underlying exception : " + generationFailed), | ||
| generationFailed); | ||
| } | ||
| if (TypeSupport.INSTANCE.isSealed(typeToMock) && typeToMock.isEnum()) { | ||
| throw new MockitoException( | ||
| join( | ||
| "Mockito cannot mock this class: " + typeToMock + ".", | ||
| "Sealed abstract enums can't be mocked. Since Java 15 abstract enums are declared sealed, which prevents mocking.", | ||
| "You can still return an existing enum literal from a stubbed method call."), | ||
| generationFailed); | ||
| } | ||
| if (TypeSupport.INSTANCE.isSealed(typeToMock) | ||
| && Modifier.isAbstract(typeToMock.getModifiers())) { | ||
| throw new MockitoException( | ||
| join( | ||
| "Mockito cannot mock this class: " + typeToMock + ".", | ||
| "Sealed interfaces or abstract classes can't be mocked. Interfaces cannot be instantiated and cannot be subclassed for mocking purposes.", | ||
| "Instead of mocking a sealed interface or an abstract class, a non-abstract class can be mocked and used to represent the interface."), | ||
| generationFailed); | ||
| } | ||
| if (Modifier.isPrivate(typeToMock.getModifiers())) { | ||
| throw new MockitoException( | ||
| join( | ||
|
|
@@ -476,6 +462,7 @@ private <T> RuntimeException prettifyFailure( | |
| "Hotspot", | ||
| ""), | ||
| Platform.describe(), | ||
| MockitoInformation.describe(MockMakers.INLINE), | ||
| "", | ||
| "You are seeing this disclaimer because Mockito is configured to create inlined mocks.", | ||
| "You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.", | ||
|
|
@@ -561,7 +548,10 @@ public TypeMockability isTypeMockable(final Class<?> type) { | |
| return new TypeMockability() { | ||
| @Override | ||
| public boolean mockable() { | ||
| return INSTRUMENTATION.isModifiableClass(type) && !EXCLUDES.contains(type); | ||
| return INSTRUMENTATION.isModifiableClass(type) | ||
| && !EXCLUDES.contains(type) | ||
| && !(Modifier.isAbstract(type.getModifiers()) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: use |
||
| && TypeSupport.INSTANCE.isSealed(type)); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -575,6 +565,15 @@ public String nonMockableReason() { | |
| if (EXCLUDES.contains(type)) { | ||
| return "Cannot mock wrapper types, String.class or Class.class"; | ||
| } | ||
| if (Modifier.isAbstract(type.getModifiers()) | ||
| && TypeSupport.INSTANCE.isSealed(type)) { | ||
| if (type.isEnum()) { | ||
| return "Sealed abstract enums can't be mocked. Since Java 15 abstract enums are declared sealed, which prevents mocking. You can still return an existing enum literal from a stubbed method call."; | ||
| } else { | ||
| return "Sealed interfaces or abstract classes can't be mocked. Interfaces cannot be instantiated and cannot be subclassed for mocking purposes. Instead of mocking a sealed interface or an abstract class, a non-abstract class can be mocked and used to represent the interface."; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: use
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I didn't it's because this string will appear has single reason prefixed by This can be worked around though. |
||
| } | ||
| } | ||
|
|
||
| return "VM does not support modification of given type"; | ||
| } | ||
| }; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ultra-nit: define constant value to use both here and in the task implementation