Skip to content

Commit

Permalink
Fix mockito-inline for Groovy projects (#2618)
Browse files Browse the repository at this point in the history
Add ignore matcher for Groovy meta methods for inline Byte Buddy mock maker.
Add a new test subproject to verify fixes between mockito-inline and Groovy

Fixes #2522

Co-authored-by: Steve Green <steve@Steves-MacBook-Pro.local>
  • Loading branch information
raphw and Steve Green committed Apr 18, 2022
1 parent 2947e9d commit d539af2
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 26 deletions.
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 @@ -12,12 +12,9 @@
import static net.bytebuddy.matcher.ElementMatchers.any;
import static net.bytebuddy.matcher.ElementMatchers.hasParameters;
import static net.bytebuddy.matcher.ElementMatchers.hasType;
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
import static net.bytebuddy.matcher.ElementMatchers.isEquals;
import static net.bytebuddy.matcher.ElementMatchers.isHashCode;
import static net.bytebuddy.matcher.ElementMatchers.isPackagePrivate;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.whereAny;
import static org.mockito.internal.util.StringUtil.join;
Expand All @@ -29,9 +26,7 @@
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Random;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.method.MethodDescription;
Expand Down Expand Up @@ -224,7 +219,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 @@ -282,22 +277,6 @@ public void mockClassConstruction(Class<?> type) {
"The subclass byte code generator cannot create construction mocks");
}

private <T> Collection<Class<? super T>> getAllTypes(Class<T> type) {
Collection<Class<? super T>> supertypes = new LinkedList<>();
supertypes.add(type);
Class<? super T> superType = type;
while (superType != null) {
supertypes.add(superType);
superType = superType.getSuperclass();
}
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() { }
}
}

0 comments on commit d539af2

Please sign in to comment.