Skip to content

Commit

Permalink
Ensure @QuarkusTest works with @nested and test lifecycle methods
Browse files Browse the repository at this point in the history
Fixes: #17975
  • Loading branch information
geoand committed Jun 18, 2021
1 parent 4237cbd commit 4a61283
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import static org.hamcrest.Matchers.contains;

import java.util.concurrent.atomic.AtomicInteger;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

Expand All @@ -11,12 +15,26 @@
@QuarkusTest
public class JaxbTestCase {

private static final AtomicInteger count = new AtomicInteger(0);

@BeforeEach
public void beforeInEnclosing() {
count.incrementAndGet();
}

@Nested
class SomeClass {

@BeforeEach
public void beforeInTest() {
count.incrementAndGet();
}

@Test
public void testNews() {
RestAssured.when().get("/test/jaxb/getnews").then()
.body("author", contains("Emmanuel Bernard"));
Assertions.assertEquals(2, count.get());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -919,34 +919,19 @@ public void interceptAfterAllMethod(Invocation<Void> invocation, ReflectiveInvoc
private Object runExtensionMethod(ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)
throws Throwable {
resetHangTimeout();
Method newMethod = null;

ClassLoader old = setCCL(runningQuarkusApplication.getClassLoader());
try {
Class<?> c = Class.forName(extensionContext.getRequiredTestClass().getName(), true,
Class<?> testClassFromTCCL = Class.forName(extensionContext.getRequiredTestClass().getName(), true,
Thread.currentThread().getContextClassLoader());
while (c != Object.class) {
if (c.getName().equals(invocationContext.getExecutable().getDeclaringClass().getName())) {
try {
Class<?>[] originalParameterTypes = invocationContext.getExecutable().getParameterTypes();
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,
Thread.currentThread().getContextClassLoader()));
}
}
newMethod = c.getDeclaredMethod(invocationContext.getExecutable().getName(),
parameterTypesFromTccl.toArray(new Class[0]));
break;
} catch (NoSuchMethodException ignored) {

}
}
c = c.getSuperclass();
Method newMethod = determineTCCLExtensionMethod(invocationContext, testClassFromTCCL);
boolean methodFromEnclosing = false;
// this is needed to support before*** and after*** methods that are part of class that encloses the test class
// (the test class is in this case a @Nested test)
if ((newMethod == null) && (testClassFromTCCL.getEnclosingClass() != null)) {
testClassFromTCCL = testClassFromTCCL.getEnclosingClass();
newMethod = determineTCCLExtensionMethod(invocationContext, testClassFromTCCL);
methodFromEnclosing = true;
}
if (newMethod == null) {
throw new RuntimeException("Could not find method " + invocationContext.getExecutable() + " on test class");
Expand Down Expand Up @@ -990,7 +975,13 @@ private Object runExtensionMethod(ReflectiveInvocationContext<Method> invocation
}
}

return newMethod.invoke(actualTestInstance, argumentsFromTccl.toArray(new Object[0]));
Object effectiveTestInstance = actualTestInstance;
if (methodFromEnclosing) {
// TODO: this is a little dodgy, ideally we would need to use the same constructor that was used for the original object
// but it's unlikely(?) we will run into this combo
effectiveTestInstance = testClassFromTCCL.getConstructor().newInstance();
}
return newMethod.invoke(effectiveTestInstance, argumentsFromTccl.toArray(new Object[0]));
} catch (InvocationTargetException e) {
throw e.getCause();
} catch (IllegalAccessException | ClassNotFoundException e) {
Expand All @@ -1000,6 +991,35 @@ private Object runExtensionMethod(ReflectiveInvocationContext<Method> invocation
}
}

private Method determineTCCLExtensionMethod(ReflectiveInvocationContext<Method> invocationContext, Class<?> c)
throws ClassNotFoundException {
Method newMethod = null;
while (c != Object.class) {
if (c.getName().equals(invocationContext.getExecutable().getDeclaringClass().getName())) {
try {
Class<?>[] originalParameterTypes = invocationContext.getExecutable().getParameterTypes();
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,
Thread.currentThread().getContextClassLoader()));
}
}
newMethod = c.getDeclaredMethod(invocationContext.getExecutable().getName(),
parameterTypesFromTccl.toArray(new Class[0]));
break;
} catch (NoSuchMethodException ignored) {

}
}
c = c.getSuperclass();
}
return newMethod;
}

@Override
public void afterAll(ExtensionContext context) throws Exception {
resetHangTimeout();
Expand Down

0 comments on commit 4a61283

Please sign in to comment.