Skip to content

Commit

Permalink
Make sure that test callbacks always work on the proper test method
Browse files Browse the repository at this point in the history
  • Loading branch information
geoand committed Jul 14, 2020
1 parent 5ffccca commit 3be2f52
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.quarkus.it.main;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;

/**
* The purpose of this test is simply to ensure that {@link SimpleAnnotationCheckerBeforeEachCallback}
* can read {@code @TestAnnotation} without issue
*/
@QuarkusTest
public class QuarkusTestCallbacksTestCase {

@Test
@TestAnnotation
public void testTestMethodHasAnnotation() {

}

@Target({ METHOD })
@Retention(RUNTIME)
public @interface TestAnnotation {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.quarkus.it.main;

import java.lang.reflect.Method;

import io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback;
import io.quarkus.test.junit.callback.QuarkusTestMethodContext;

public class SimpleAnnotationCheckerBeforeEachCallback implements QuarkusTestBeforeEachCallback {

@Override
public void beforeEach(QuarkusTestMethodContext context) {
// make sure that this comes into play only for the test we care about

Method testMethod = context.getTestMethod();
if (!testMethod.getDeclaringClass().getName().endsWith("QuarkusTestCallbacksTestCase")) {
return;
}

if (!testMethod.getName().equals("testTestMethodHasAnnotation")) {
return;
}

QuarkusTestCallbacksTestCase.TestAnnotation annotation = testMethod
.getAnnotation(QuarkusTestCallbacksTestCase.TestAnnotation.class);
if (annotation == null) {
throw new IllegalStateException(
"Expected to find annotation @TestAnnotation on method test method testTestMethodHasAnnotation");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.quarkus.it.main.SimpleAnnotationCheckerBeforeEachCallback
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ public class QuarkusTestExtension
private static Path testClassLocation;
private static Throwable firstException; //if this is set then it will be thrown from the very first test that is run, the rest are aborted

private static List<Object> beforeAllCallbacks = new ArrayList<>();
private static List<Object> beforeEachCallbacks = new ArrayList<>();
private static List<Object> afterEachCallbacks = new ArrayList<>();
private static List<Object> beforeAllCallbacks;
private static List<Object> beforeEachCallbacks;
private static List<Object> afterEachCallbacks;
private static Class<?> quarkusTestMethodContextClass;
private static Class<? extends QuarkusTestProfile> quarkusTestProfile;

Expand Down Expand Up @@ -293,6 +293,13 @@ private void populateDeepCloneField(StartupAction startupAction) {
}

private void populateCallbacks(ClassLoader classLoader) throws ClassNotFoundException {
// make sure that we start over everytime we populate the callbacks
// otherwise previous runs of QuarkusTest (with different TestProfile values can leak into the new run)
quarkusTestMethodContextClass = null;
beforeAllCallbacks = new ArrayList<>();
beforeEachCallbacks = new ArrayList<>();
afterEachCallbacks = new ArrayList<>();

ServiceLoader<?> quarkusTestBeforeAllLoader = ServiceLoader
.load(Class.forName(QuarkusTestBeforeAllCallback.class.getName(), false, classLoader), classLoader);
for (Object quarkusTestBeforeAllCallback : quarkusTestBeforeAllLoader) {
Expand Down Expand Up @@ -363,14 +370,45 @@ public void afterEach(ExtensionContext context) throws Exception {
}
}

// We need the usual ClassLoader hacks in order to present the callbacks with the proper test object and context
private Map.Entry<Class<?>, ?> createQuarkusTestMethodContextTuple(ExtensionContext context) throws Exception {
ClassLoader classLoader = runningQuarkusApplication.getClassLoader();
if (quarkusTestMethodContextClass == null) {
quarkusTestMethodContextClass = Class.forName(QuarkusTestMethodContext.class.getName(), true,
runningQuarkusApplication.getClassLoader());
quarkusTestMethodContextClass = Class.forName(QuarkusTestMethodContext.class.getName(), true, classLoader);
}

Method originalTestMethod = context.getRequiredTestMethod();
Class<?>[] originalParameterTypes = originalTestMethod.getParameterTypes();
Method actualTestMethod = null;

// go up the class hierarchy to fetch the proper test method
Class<?> c = actualTestClass;
List<Class<?>> parameterTypesFromTccl = new ArrayList<>(originalParameterTypes.length);
for (Class<?> type : originalParameterTypes) {
if (type.isPrimitive()) {
parameterTypesFromTccl.add(type);
} else {
parameterTypesFromTccl
.add(Class.forName(type.getName(), true, classLoader));
}
}
Class<?>[] parameterTypes = parameterTypesFromTccl.toArray(new Class[0]);
while (c != Object.class) {
try {
actualTestMethod = c.getDeclaredMethod(originalTestMethod.getName(), parameterTypes);
break;
} catch (NoSuchMethodException ignored) {

}
c = c.getSuperclass();
}
if (actualTestMethod == null) {
throw new RuntimeException("Could not find method " + originalTestMethod + " on test class");
}

Constructor<?> constructor = quarkusTestMethodContextClass.getConstructor(Object.class, Method.class);
return new AbstractMap.SimpleEntry<>(quarkusTestMethodContextClass,
constructor.newInstance(actualTestInstance, context.getRequiredTestMethod()));
constructor.newInstance(actualTestInstance, actualTestMethod));
}

private boolean isNativeTest(ExtensionContext context) {
Expand Down

0 comments on commit 3be2f52

Please sign in to comment.