Skip to content

Commit

Permalink
Introduce basic support for @after methods in tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrannen committed Oct 28, 2015
1 parent 699d164 commit 618cffc
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 138 deletions.
@@ -1,5 +1,18 @@
package org.junit.gen5.engine.junit5; package org.junit.gen5.engine.junit5;


import static java.lang.String.*;
import static java.util.stream.Collectors.*;
import static org.junit.gen5.commons.util.ReflectionUtils.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.junit.gen5.api.After;
import org.junit.gen5.api.Before; import org.junit.gen5.api.Before;
import org.junit.gen5.api.Test; import org.junit.gen5.api.Test;
import org.junit.gen5.engine.TestDescriptor; import org.junit.gen5.engine.TestDescriptor;
Expand All @@ -9,139 +22,116 @@
import org.opentestalliance.TestAbortedException; import org.opentestalliance.TestAbortedException;
import org.opentestalliance.TestSkippedException; import org.opentestalliance.TestSkippedException;


import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import static org.junit.gen5.commons.util.ReflectionUtils.invokeMethod;
import static org.junit.gen5.commons.util.ReflectionUtils.newInstance;

public class JUnit5TestEngine implements TestEngine { public class JUnit5TestEngine implements TestEngine {
// TODO - SBE - could be replace by JUnit5TestEngine.class.getCanonicalName();
private static final String JUNIT5_ENGINE_ID = "junit5";

@Override
public String getId() {
return JUNIT5_ENGINE_ID;
}

@Override
public List<TestDescriptor> discoverTests(TestPlanSpecification specification) {
List<Class<?>> testClasses = fetchTestClasses(specification);

List<TestDescriptor> testDescriptors = testClasses.stream()
.map(Class::getDeclaredMethods)
.flatMap(Arrays::stream)
.filter(method -> method.isAnnotationPresent(Test.class))
.map(method -> new JavaTestDescriptor(getId(), method))
.collect(toList());

testDescriptors.addAll(
specification.getUniqueIds().stream()
.map(JavaTestDescriptor::from)
.collect(toList())
);

return testDescriptors;
}

private List<Class<?>> fetchTestClasses(TestPlanSpecification testPlanSpecification) {
List<Class<?>> testClasses = new LinkedList<>();

// Add specified test classes directly
testClasses.addAll(testPlanSpecification.getClasses());

// Add test classes by name
for (String className : testPlanSpecification.getClassNames()) {
try {
testClasses.add(Class.forName(className));
} catch (ClassNotFoundException e) {
String msg = "Could not find test class '%s' in the classpath!";
throw new IllegalArgumentException(format(msg, className));
}
}

// TODO - SBE - Add classes for packages
// TODO - SBE - Add classes for package names
// TODO - SBE - Add classes for paths
// TODO - SBE - Add classes for file names

return testClasses;
}

@Override
public boolean supports(TestDescriptor testDescriptor) {
return testDescriptor instanceof JavaTestDescriptor;
}

@Override
public void execute(Collection<TestDescriptor> testDescriptors, TestExecutionListener testExecutionListener) {
for (TestDescriptor testDescriptor : testDescriptors) {
try {
testExecutionListener.testStarted(testDescriptor);

JavaTestDescriptor javaTestDescriptor = (JavaTestDescriptor) testDescriptor;
this.handleTestExecution(javaTestDescriptor);
testExecutionListener.testSucceeded(testDescriptor);
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof TestSkippedException) {
testExecutionListener.testSkipped(testDescriptor, e.getTargetException());
} else if (e.getTargetException() instanceof TestAbortedException) {
testExecutionListener.testAborted(testDescriptor, e.getTargetException());
} else {
testExecutionListener.testFailed(testDescriptor, e.getTargetException());
}
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException e) {
throw new IllegalArgumentException(
String.format("Test %s not well-formed and cannot be executed! ", testDescriptor.getUniqueId()));
}
}
}


protected void handleTestExecution(JavaTestDescriptor javaTestDescriptor) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class<?> testClass = javaTestDescriptor.getTestClass();
Object testInstance = newInstance(testClass);

this.handleBeforeMethods(testClass, testInstance);
this.handleTestMethod(javaTestDescriptor, testInstance);

}

private void handleTestMethod(JavaTestDescriptor javaTestDescriptor, Object testInstance) throws IllegalAccessException, InvocationTargetException {
invokeMethod(javaTestDescriptor.getTestMethod(), testInstance);
}

private void handleBeforeMethods(Class<?> testClass, Object testInstance) throws IllegalAccessException, InvocationTargetException {
List<Method> beforeMethods = this.findBeforeMethods(testClass);

System.out.println("BEFORE METHODS: " + beforeMethods);

for (Method method: beforeMethods) {
invokeMethod(method, testInstance);
}
}

private List<Method> findBeforeMethods(Class<?> testClass) {
return this.findMethodsForGivenAnnotation(testClass, Before.class);
}



private List<Method> findMethodsForGivenAnnotation(Class<?> testClass, Class<? extends Annotation> desiredAnnotation) {
List<Method> methods = new ArrayList<>();

//TODO port to streams?
for(Method method: testClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(desiredAnnotation))
methods.add(method);
}

return methods;
}



} // TODO - SBE - could be replaced by JUnit5TestEngine.class.getCanonicalName()
private static final String ENGINE_ID = "junit5";

@Override
public String getId() {
return ENGINE_ID;
}

@Override
public List<TestDescriptor> discoverTests(TestPlanSpecification specification) {
List<Class<?>> testClasses = discoverTestClasses(specification);

List<TestDescriptor> testDescriptors = testClasses.stream()
.map(Class::getDeclaredMethods)
.flatMap(Arrays::stream)
.filter(method -> method.isAnnotationPresent(Test.class))
.map(method -> new JavaTestDescriptor(getId(), method))
.collect(toList());

testDescriptors.addAll(
specification.getUniqueIds().stream()
.map(JavaTestDescriptor::from)
.collect(toList())
);

return testDescriptors;
}

private List<Class<?>> discoverTestClasses(TestPlanSpecification testPlanSpecification) {
List<Class<?>> testClasses = new ArrayList<>();

// Add specified test classes directly
testClasses.addAll(testPlanSpecification.getClasses());

// Add test classes by name
for (String className : testPlanSpecification.getClassNames()) {
try {
testClasses.add(Class.forName(className));
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(format("Failed to load test class '%s'", className));
}
}

return testClasses;
}

@Override
public boolean supports(TestDescriptor testDescriptor) {
return testDescriptor instanceof JavaTestDescriptor;
}

@Override
public void execute(Collection<TestDescriptor> testDescriptors, TestExecutionListener testExecutionListener) {
for (TestDescriptor testDescriptor : testDescriptors) {
try {
testExecutionListener.testStarted(testDescriptor);

JavaTestDescriptor javaTestDescriptor = (JavaTestDescriptor) testDescriptor;
executeTest(javaTestDescriptor);
testExecutionListener.testSucceeded(testDescriptor);
}
catch (InvocationTargetException ex) {
Throwable targetException = ex.getTargetException();
if (targetException instanceof TestSkippedException) {
testExecutionListener.testSkipped(testDescriptor, targetException);
}
else if (targetException instanceof TestAbortedException) {
testExecutionListener.testAborted(testDescriptor, targetException);
}
else {
testExecutionListener.testFailed(testDescriptor, targetException);
}
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException e) {
throw new IllegalStateException(
String.format("Test %s is not well-formed and cannot be executed", testDescriptor.getUniqueId()));
} catch (Exception ex) {
testExecutionListener.testFailed(testDescriptor, ex);
}
}
}

protected void executeTest(JavaTestDescriptor javaTestDescriptor) throws Exception {
Class<?> testClass = javaTestDescriptor.getTestClass();

// TODO Extract test instantiation
Object testInstance = newInstance(testClass);

executeBeforeMethods(testClass, testInstance);
invokeMethod(javaTestDescriptor.getTestMethod(), testInstance);
executeAfterMethods(testClass, testInstance);
}

private void executeBeforeMethods(Class<?> testClass, Object testInstance) throws Exception {
for (Method method: findAnnotatedMethods(testClass, Before.class)) {
invokeMethod(method, testInstance);
}
}

private void executeAfterMethods(Class<?> testClass, Object testInstance) throws Exception {
for (Method method : findAnnotatedMethods(testClass, After.class)) {
invokeMethod(method, testInstance);
}
}

private List<Method> findAnnotatedMethods(Class<?> testClass, Class<? extends Annotation> annotationType) {
return Arrays.stream(testClass.getDeclaredMethods())
.filter(method -> method.isAnnotationPresent(annotationType))
.collect(toList());
}

}
31 changes: 27 additions & 4 deletions sample-project/src/test/java/com/example/SampleTestCase.java
Expand Up @@ -4,6 +4,7 @@
import static org.junit.gen5.api.Assertions.*; import static org.junit.gen5.api.Assertions.*;
import static org.junit.gen5.api.Assumptions.*; import static org.junit.gen5.api.Assumptions.*;


import org.junit.gen5.api.After;
import org.junit.gen5.api.Before; import org.junit.gen5.api.Before;
import org.junit.gen5.api.Test; import org.junit.gen5.api.Test;
import org.opentestalliance.TestSkippedException; import org.opentestalliance.TestSkippedException;
Expand All @@ -13,17 +14,39 @@
*/ */
class SampleTestCase { class SampleTestCase {


boolean setupCalled = false; static boolean staticBeforeInvoked = false;


boolean beforeInvoked = false;

boolean afterInvoked = false;

boolean throwExceptionInAfterMethod = false;


@Before
static void staticBefore() {
staticBeforeInvoked = true;
}


@Before @Before
void setup() { void before() {
this.setupCalled = true; this.beforeInvoked = true;
}

@After
void after() {
this.afterInvoked = true;
if (throwExceptionInAfterMethod) {
throw new RuntimeException("Exception thrown from @After method");
}
} }


@Test @Test
void methodLevelCallbacks() { void methodLevelCallbacks() {
assertTrue(this.setupCalled, "@Before was not invoked"); assertTrue(this.beforeInvoked, "@Before was not invoked on instance method");
assertTrue(staticBeforeInvoked, "@Before was not invoked on static method");
assertFalse(this.afterInvoked, "@After should not have been invoked");
throwExceptionInAfterMethod = true;
} }


@Test @Test
Expand Down

0 comments on commit 618cffc

Please sign in to comment.