Skip to content
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

#2522: mockito-inline & Groovy NullPointerException #2566

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions settings.gradle.kts
Expand Up @@ -6,6 +6,7 @@ include("inline",
"proxy",
"extTest",
"groovyTest",
"groovyInlineTest",
"kotlinTest",
"kotlinReleaseCoroutinesTest",
"android",
Expand Down
Expand Up @@ -4,6 +4,12 @@
*/
package org.mockito.internal.creation.bytebuddy;

import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;

import static net.bytebuddy.matcher.ElementMatchers.*;
import static net.bytebuddy.matcher.ElementMatchers.named;

public interface BytecodeGenerator {

<T> Class<? extends T> mockClass(MockFeatures<T> features);
Expand All @@ -13,4 +19,16 @@ public interface BytecodeGenerator {
void mockClassStatic(Class<?> type);

default void clearAllCaches() {}

static ElementMatcher<MethodDescription> isGroovyMethod(boolean inline) {
ElementMatcher.Junction<MethodDescription> matcher =
isDeclaredBy(named("groovy.lang.GroovyObjectSupport"))
.or(isAnnotatedWith(named("groovy.transform.Internal")));
if (inline) {
return matcher.or(
named("$getStaticMetaClass").and(returns(named("groovy.lang.MetaClass"))));
} else {
return matcher;
}
}
}
Expand Up @@ -117,15 +117,19 @@ public InlineBytecodeGenerator(
.or(isEquals())
.or(isDefaultFinalizer())))
.and(
not(
isDeclaredBy(nameStartsWith("java."))
not(isDeclaredBy(nameStartsWith("java."))
.<MethodDescription>and(
isPackagePrivate()))),
isPackagePrivate()))
.and(
not(
BytecodeGenerator
.isGroovyMethod(
true)))),
Advice.withCustomMapping()
.bind(MockMethodAdvice.Identifier.class, identifier)
.to(MockMethodAdvice.class))
.method(
isStatic(),
isStatic().and(not(BytecodeGenerator.isGroovyMethod(true))),
Advice.withCustomMapping()
.bind(MockMethodAdvice.Identifier.class, identifier)
.to(MockMethodAdvice.ForStatic.class))
Expand Down Expand Up @@ -387,6 +391,7 @@ public byte[] transform(
// ,ClassFileLocator.ForClassLoader.ofSystemLoader()
// )
)
.ignoreAlso(BytecodeGenerator.isGroovyMethod())
// Note: The VM erases parameter meta data from the provided class file
// (bug). We just add this information manually.
.visit(new ParameterWritingVisitorWrapper(classBeingRedefined))
Expand Down
Expand Up @@ -224,7 +224,7 @@ public <T> Class<? extends T> mockClass(MockFeatures<T> features) {
byteBuddy
.subclass(features.mockedType)
.name(name)
.ignoreAlso(isGroovyMethod())
.ignoreAlso(BytecodeGenerator.isGroovyMethod(false))
.annotateType(
features.stripAnnotations
? new Annotation[0]
Expand Down Expand Up @@ -293,11 +293,6 @@ private <T> Collection<Class<? super T>> getAllTypes(Class<T> type) {
return supertypes;
}

private static ElementMatcher<MethodDescription> isGroovyMethod() {
return isDeclaredBy(named("groovy.lang.GroovyObjectSupport"))
.or(isAnnotatedWith(named("groovy.transform.Internal")));
}

private boolean isComingFromJDK(Class<?> type) {
// Comes from the manifest entry :
// Implementation-Title: Java Runtime Environment
Expand Down
11 changes: 11 additions & 0 deletions subprojects/groovyInlineTest/groovyInlineTest.gradle
@@ -0,0 +1,11 @@
apply plugin: 'groovy'

description = "Integration test for using mockito-inline with Groovy."

apply from: "$rootDir/gradle/dependencies.gradle"

dependencies {
testImplementation project(":inline")
testImplementation libraries.groovy
testImplementation libraries.junit4
}
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2022 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.groovy

import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner

import static org.mockito.Mockito.verify

@RunWith(MockitoJUnitRunner)
class GroovyMockitoTest {

@Mock Helper helper
@InjectMocks ClassUnderTest classUnderTest

/**
* Test that the Groovy class under test can call methods on a mocked Groovy
* helper class.
*/
@Test
void testCallGroovyFromGroovy() {
classUnderTest.methodUnderTest()
verify(helper).helperMethod()
}

static class ClassUnderTest {
private final Helper helper

ClassUnderTest(Helper helper) {
this.helper = helper
}

void methodUnderTest() {
helper.helperMethod()
}
}

static class Helper {
void helperMethod() { }
}
}