Skip to content

Instantly share code, notes, and snippets.

@thomasdarimont
Last active August 29, 2015 13:57
Show Gist options
  • Save thomasdarimont/9bf439fa3c48f110b49e to your computer and use it in GitHub Desktop.
Save thomasdarimont/9bf439fa3c48f110b49e to your computer and use it in GitHub Desktop.
package de.jugsaar.meeting8.testing.junit.poc;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import de.jugsaar.meeting8.testing.junit.poc.SpringContext.Use;
import de.jugsaar.meeting8.testing.junit.poc.SpringJunit4RunnerRuleTests.Nested.SpecialConfig;
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class SpringJunit4RunnerRuleTests {
@ClassRule public static SpringContext springContext = new SpringContext();
@Autowired Foo foo;
@Test
public void test1_should_use_ConcreteFoo() {
Assert.assertNotNull(foo);
Assert.assertTrue(foo instanceof ConcreteFoo);
}
@Test
@Use(SpecialConfig.class)
public void test2_should_use_SpecialFoo() {
Assert.assertNotNull(foo);
Assert.assertTrue(foo instanceof SpecialFoo);
}
@Test
public void test3_should_use_ConcreteFoo() {
Assert.assertNotNull(foo);
Assert.assertTrue(foo instanceof ConcreteFoo);
}
static interface Foo {}
static class ConcreteFoo implements Foo {};
static class SpecialFoo implements Foo {}
@Configuration
static class Config {
@Bean
public Foo foo() {
return new ConcreteFoo();
}
}
// Not visible for the test directly
public static class Nested {
@Configuration
public static class SpecialConfig extends Config {
@Bean
public Foo foo() {
return new SpecialFoo();
}
}
}
}
package de.jugsaar.meeting8.testing.junit.poc;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.util.ReflectionUtils;
public class SpringContext implements TestRule {
private Class<?> testClass;
private volatile boolean initilized;
public SpringContext() {
this(null);
}
public SpringContext(Class<?> testClass) {
this.testClass = testClass;
}
@Override
public Statement apply(final Statement base, Description description) {
if (initilized) {
return base;
}
initilized = true;
invokeSpringRunner(base, description);
return noop();
}
private void invokeSpringRunner(final Statement base, Description description) {
try {
Field runNotifierField = ReflectionUtils.findField(base.getClass(), "val$notifier");
ReflectionUtils.makeAccessible(runNotifierField);
Object runNotifier = ReflectionUtils.getField(runNotifierField, base);
ExtendedSpringJUnit4ClassRunner runner = new ExtendedSpringJUnit4ClassRunner(
this.testClass != null ? this.testClass : description.getTestClass());
runner.run(RunNotifier.class.cast(runNotifier));
} catch (InitializationError e) {
throw new RuntimeException(e);
}
}
private Statement noop() {
return new Statement() {
@Override
public void evaluate() throws Throwable {}
};
}
static class ExtendedSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner {
public ExtendedSpringJUnit4ClassRunner(Class<?> clazz) throws InitializationError {
super(clazz);
getTestContextManager().getTestExecutionListeners().add(new NestedContextTestExecutionListener());
}
}
static class NestedContextTestExecutionListener extends AbstractTestExecutionListener {
@Override
public void beforeTestMethod(TestContext testContext) throws Exception {
Use use = testContext.getTestMethod().getAnnotation(Use.class);
if (use != null && use.value().length > 0) {
AnnotationConfigApplicationContext ctxt = new AnnotationConfigApplicationContext();
ctxt.setParent(testContext.getApplicationContext());
for (Class<?> additionalConfigClass : use.value()) {
ctxt.register(additionalConfigClass);
}
ctxt.refresh();
ctxt.getAutowireCapableBeanFactory().autowireBean(testContext.getTestInstance());
}
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public static @interface Use {
Class<?>[] value() default {};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment