Skip to content

Commit

Permalink
Eagerly load and validate @beforeeach & @AfterEach methods
Browse files Browse the repository at this point in the history
With this commit @beforeeach and @AfterEach methods are looked up and
validated eagerly during the discovery phase instead of late in the game
during the execution phase.

This commit also extracts a new LifecycleMethodUtils class from the
ClassTestDescriptor.

Issue: #232
  • Loading branch information
sbrannen committed May 2, 2016
1 parent b691e6a commit 6b575f2
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,27 @@
package org.junit.gen5.engine.junit5.descriptor;

import static org.junit.gen5.commons.meta.API.Usage.Internal;
import static org.junit.gen5.commons.util.AnnotationUtils.findAnnotatedMethods;
import static org.junit.gen5.engine.junit5.descriptor.LifecycleMethodUtils.findAfterAllMethods;
import static org.junit.gen5.engine.junit5.descriptor.LifecycleMethodUtils.findAfterEachMethods;
import static org.junit.gen5.engine.junit5.descriptor.LifecycleMethodUtils.findBeforeAllMethods;
import static org.junit.gen5.engine.junit5.descriptor.LifecycleMethodUtils.findBeforeEachMethods;
import static org.junit.gen5.engine.junit5.execution.MethodInvocationContextFactory.methodInvocationContext;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;

import org.junit.gen5.api.AfterAll;
import org.junit.gen5.api.AfterEach;
import org.junit.gen5.api.BeforeAll;
import org.junit.gen5.api.BeforeEach;
import org.junit.gen5.api.extension.AfterAllCallback;
import org.junit.gen5.api.extension.BeforeAllCallback;
import org.junit.gen5.api.extension.ConditionEvaluationResult;
import org.junit.gen5.api.extension.ContainerExtensionContext;
import org.junit.gen5.api.extension.Extension;
import org.junit.gen5.api.extension.ExtensionConfigurationException;
import org.junit.gen5.api.extension.TestExtensionContext;
import org.junit.gen5.commons.JUnitException;
import org.junit.gen5.commons.meta.API;
import org.junit.gen5.commons.util.ExceptionUtils;
import org.junit.gen5.commons.util.Preconditions;
import org.junit.gen5.commons.util.ReflectionUtils;
import org.junit.gen5.commons.util.ReflectionUtils.MethodSortOrder;
import org.junit.gen5.engine.TestDescriptor;
import org.junit.gen5.engine.TestTag;
import org.junit.gen5.engine.UniqueId;
Expand Down Expand Up @@ -70,6 +64,10 @@ public class ClassTestDescriptor extends JUnit5TestDescriptor implements Contain

private final List<Method> afterAllMethods;

private final List<Method> beforeEachMethods;

private final List<Method> afterEachMethods;

public ClassTestDescriptor(UniqueId uniqueId, Class<?> testClass) {
super(uniqueId);

Expand All @@ -78,6 +76,8 @@ public ClassTestDescriptor(UniqueId uniqueId, Class<?> testClass) {

this.beforeAllMethods = findBeforeAllMethods(testClass);
this.afterAllMethods = findAfterAllMethods(testClass);
this.beforeEachMethods = findBeforeEachMethods(testClass);
this.afterEachMethods = findAfterEachMethods(testClass);

setSource(new JavaSource(testClass));
}
Expand Down Expand Up @@ -113,7 +113,7 @@ public final boolean isContainer() {

@Override
public JUnit5EngineExecutionContext prepare(JUnit5EngineExecutionContext context) {
ExtensionRegistry registry = populateNewExtensionRegistryFromExtendWith(testClass,
ExtensionRegistry registry = populateNewExtensionRegistryFromExtendWith(this.testClass,
context.getExtensionRegistry());

registerBeforeEachMethodAdapters(registry);
Expand Down Expand Up @@ -176,23 +176,15 @@ private void invokeBeforeAllCallbacks(ExtensionRegistry registry, ContainerExten
}

private void invokeBeforeAllMethods(ExtensionRegistry registry, ContainerExtensionContext context) {
for (Method method : this.beforeAllMethods) {
try {
new MethodInvoker(context, registry).invoke(methodInvocationContext(null, method));
}
catch (Throwable throwable) {
ExceptionUtils.throwAsUncheckedException(throwable);
}
}
this.beforeAllMethods.forEach(method -> executeAndMaskThrowable(
() -> new MethodInvoker(context, registry).invoke(methodInvocationContext(null, method))));
}

private void invokeAfterAllMethods(ExtensionRegistry registry, ContainerExtensionContext context,
ThrowableCollector throwableCollector) {

for (Method method : this.afterAllMethods) {
throwableCollector.execute(
() -> new MethodInvoker(context, registry).invoke(methodInvocationContext(null, method)));
}
this.afterAllMethods.forEach(method -> throwableCollector.execute(
() -> new MethodInvoker(context, registry).invoke(methodInvocationContext(null, method))));
}

private void invokeAfterAllCallbacks(ExtensionRegistry registry, ContainerExtensionContext context,
Expand All @@ -202,27 +194,18 @@ private void invokeAfterAllCallbacks(ExtensionRegistry registry, ContainerExtens
.forEach(extension -> throwableCollector.execute(() -> extension.afterAll(context)));
}

private void registerBeforeEachMethodAdapters(ExtensionRegistry extensionRegistry) {
registerAnnotatedMethodsAsExtensions(extensionRegistry, BeforeEach.class, BeforeEachMethodAdapter.class,
this::assertNonStatic, this::synthesizeBeforeEachMethodAdapter);
private void registerBeforeEachMethodAdapters(ExtensionRegistry registry) {
registerMethodsAsExtensions(this.beforeEachMethods, registry, this::synthesizeBeforeEachMethodAdapter);
}

private void registerAfterEachMethodAdapters(ExtensionRegistry extensionRegistry) {
registerAnnotatedMethodsAsExtensions(extensionRegistry, AfterEach.class, AfterEachMethodAdapter.class,
this::assertNonStatic, this::synthesizeAfterEachMethodAdapter);
private void registerAfterEachMethodAdapters(ExtensionRegistry registry) {
registerMethodsAsExtensions(this.afterEachMethods, registry, this::synthesizeAfterEachMethodAdapter);
}

private <E extends Extension> void registerAnnotatedMethodsAsExtensions(ExtensionRegistry extensionRegistry,
Class<? extends Annotation> annotationType, Class<E> extensionType,
BiConsumer<Class<E>, Method> methodValidator,
private void registerMethodsAsExtensions(List<Method> methods, ExtensionRegistry registry,
BiFunction<ExtensionRegistry, Method, Extension> extensionSynthesizer) {

// @formatter:off
findAnnotatedMethods(testClass, annotationType, MethodSortOrder.HierarchyDown).stream()
.peek(method -> methodValidator.accept(extensionType, method))
.forEach(method ->
extensionRegistry.registerExtension(extensionSynthesizer.apply(extensionRegistry, method), method));
// @formatter:on
methods.forEach(method -> registry.registerExtension(extensionSynthesizer.apply(registry, method), method));
}

private BeforeEachMethodAdapter synthesizeBeforeEachMethodAdapter(ExtensionRegistry registry, Method method) {
Expand All @@ -243,32 +226,4 @@ private void invokeMethodInTestExtensionContext(Method method, TestExtensionCont
new MethodInvoker(context, registry).invoke(methodInvocationContext(instance, method));
}

private void assertNonStatic(Class<? extends Extension> extensionType, Method method) {
if (ReflectionUtils.isStatic(method)) {
String message = String.format("Cannot register method '%s' as a(n) %s since it is static.",
method.getName(), extensionType.getSimpleName());
throw new ExtensionConfigurationException(message);
}
}

private static void assertStatic(Class<? extends Annotation> annotationType, Method method) {
if (!ReflectionUtils.isStatic(method)) {
String message = String.format("Cannot register method '%s' as an @%s method since it is not static.",
method.getName(), annotationType.getSimpleName());
throw new ExtensionConfigurationException(message);
}
}

private static List<Method> findBeforeAllMethods(Class<?> testClass) {
List<Method> methods = findAnnotatedMethods(testClass, BeforeAll.class, MethodSortOrder.HierarchyDown);
methods.forEach(method -> assertStatic(BeforeAll.class, method));
return methods;
}

private static List<Method> findAfterAllMethods(Class<?> testClass) {
List<Method> methods = findAnnotatedMethods(testClass, AfterAll.class, MethodSortOrder.HierarchyUp);
methods.forEach(method -> assertStatic(AfterAll.class, method));
return methods;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2015-2016 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/

package org.junit.gen5.engine.junit5.descriptor;

import static org.junit.gen5.commons.util.AnnotationUtils.findAnnotatedMethods;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;

import org.junit.gen5.api.AfterAll;
import org.junit.gen5.api.AfterEach;
import org.junit.gen5.api.BeforeAll;
import org.junit.gen5.api.BeforeEach;
import org.junit.gen5.commons.JUnitException;
import org.junit.gen5.commons.util.ReflectionUtils;
import org.junit.gen5.commons.util.ReflectionUtils.MethodSortOrder;

/**
* Collection of utilities for working with test lifecycle methods.
*
* @since 5.0
*/
final class LifecycleMethodUtils {

private LifecycleMethodUtils() {
/* no-op */
}

static List<Method> findBeforeAllMethods(Class<?> testClass) {
List<Method> methods = findAnnotatedMethods(testClass, BeforeAll.class, MethodSortOrder.HierarchyDown);
methods.forEach(method -> assertStatic(BeforeAll.class, method));
return methods;
}

static List<Method> findAfterAllMethods(Class<?> testClass) {
List<Method> methods = findAnnotatedMethods(testClass, AfterAll.class, MethodSortOrder.HierarchyUp);
methods.forEach(method -> assertStatic(AfterAll.class, method));
return methods;
}

static List<Method> findBeforeEachMethods(Class<?> testClass) {
List<Method> methods = findAnnotatedMethods(testClass, BeforeEach.class, MethodSortOrder.HierarchyDown);
methods.forEach(method -> assertNonStatic(BeforeEach.class, method));
return methods;
}

static List<Method> findAfterEachMethods(Class<?> testClass) {
List<Method> methods = findAnnotatedMethods(testClass, AfterEach.class, MethodSortOrder.HierarchyUp);
methods.forEach(method -> assertNonStatic(AfterEach.class, method));
return methods;
}

private static void assertStatic(Class<? extends Annotation> annotationType, Method method) {
if (!ReflectionUtils.isStatic(method)) {
throw new JUnitException(String.format("@%s method '%s' must be static.", annotationType.getSimpleName(),
method.toGenericString()));
}
}

private static void assertNonStatic(Class<? extends Annotation> annotationType, Method method) {
if (ReflectionUtils.isStatic(method)) {
throw new JUnitException(String.format("@%s method '%s' must not be static.",
annotationType.getSimpleName(), method.toGenericString()));
}
}

}

0 comments on commit 6b575f2

Please sign in to comment.