Skip to content

Instantly share code, notes, and snippets.

@jfuerth
Created March 13, 2014 18:25
Show Gist options
  • Save jfuerth/9533949 to your computer and use it in GitHub Desktop.
Save jfuerth/9533949 to your computer and use it in GitHub Desktop.
package org.uberfire.client.workbench;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Qualifier;
import org.jboss.errai.ioc.client.container.BeanProvider;
import org.jboss.errai.ioc.client.container.CreationalContext;
import org.jboss.errai.ioc.client.container.SyncBeanManager;
import org.jboss.errai.ioc.client.container.SyncBeanManagerImpl;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.FrameworkField;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import com.google.gwtmockito.GwtMockitoTestRunner;
/**
* The beginnings of a test runner for unit-testing code that depends on ErraiIOC.
* <p>
* Scans the JUnit 4 test class for {@code @Produces} fields (could be extended to methods) and inserts them as
* singleton beans into a brand new Errai bean manager. Can also inject the bean manager into a field of the test class.
*
* @author jfuerth
*/
public class ErraiIocTestRunner extends GwtMockitoTestRunner {
public static final class ErraiBeanManagementTestRule implements TestRule {
private final TestClass testClass;
private final Object target;
/**
* Creates an instance of {@code ErraiBeanManagementTestRule} in the same classloader that loaded the
* {@code testClass}. This is important in order for the BeanManager to be injectable into {@code testClass}.
*
* @param testClass
* the class that contains the JUnit 4 {@code @Test} methods. The returned Rule will have the same
* classloader as this class.
* @param target
* The instance of {@code testClass} that will receive the method call for the current test.
*/
public static TestRule createInTestClassloader(final TestClass testClass, final Object target) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException {
Object instance = Class.forName(ErraiBeanManagementTestRule.class.getName(), true, testClass.getJavaClass().getClassLoader()).getConstructors()[0].newInstance( testClass, target );
return (TestRule) instance;
}
public ErraiBeanManagementTestRule(final TestClass testClass, final Object target) {
this.testClass = testClass;
this.target = target;
}
@Override
public Statement apply( final Statement base, Description description ) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
SyncBeanManager bm = createBeanManager( testClass, target );
System.out.println("Got beans: " + bm.lookupBeans( Object.class ));
performInjections( testClass, bm, target );
base.evaluate();
}
};
}
/**
* Creates and populates a bean manager that only knows about beans produced by fields of the given test class.
* The field themselves can be populated by field initializers, Mockito ({@code @Mock}) or GWTMockito (
* {@code @GwtMock}), or in a {@code @Before} method.
*
* @param testClass
* the class that contains the JUnit 4 {@code @Test} methods.
* @param target
* The instance of {@code testClass} that will receive the method call for the current test.
* @return an Errai bean manager that only knows about beans produced by fields of {@code testClass}.
*/
private SyncBeanManager createBeanManager(final TestClass testClass, final Object target) throws IllegalArgumentException, IllegalAccessException {
System.out.println("Creating bean manager for " + target);
SyncBeanManagerImpl bm = new SyncBeanManagerImpl();
for (final FrameworkField field : testClass.getAnnotatedFields( Produces.class )) {
field.getField().setAccessible( true );
List<Annotation> qualifiers = new ArrayList<Annotation>();
for (Annotation a : field.getAnnotations()) {
if (a.annotationType().isAnnotationPresent( Qualifier.class )) {
qualifiers.add(a);
}
}
final Object fieldValue = field.get( target );
BeanProvider<Object> provider = new BeanProvider<Object>() {
@Override
public Object getInstance( CreationalContext context ) {
try {
// XXX this isn't exactly right, because for singletons, the provider only
// handles bm.newInstance(). This ought to return a new instance!
return fieldValue;
} catch ( Exception e ) {
throw new RuntimeException( e );
}
}
};
bm.addBean( (Class<Object>) fieldValue.getClass(),
(Class<Object>) field.getType(), provider, fieldValue,
qualifiers.toArray( new Annotation[qualifiers.size()] ) );
}
return bm;
}
// TODO support injecting into any class, not just the test class (so beans within bm can be wired together)
private void performInjections( TestClass testClass, SyncBeanManager bm, Object target ) throws IllegalArgumentException, IllegalAccessException, ClassNotFoundException {
for ( final FrameworkField field : testClass.getAnnotatedFields( Inject.class ) ) {
if ( field.isStatic() ) {
throw new RuntimeException( "Field " + field.getField() + " annotated with @Inject is static. That's not allowed." );
}
Class<?> syncBeanManagerType = SyncBeanManagerImpl.class; //Class.forName( SyncBeanManagerImpl.class.getName(), true, Thread.currentThread().getContextClassLoader() );
if ( field.getType().isAssignableFrom( syncBeanManagerType ) ) {
field.getField().setAccessible( true );
field.getField().set( target, bm );
} else {
// TODO: lookup in bm
throw new RuntimeException( "Field " + field.getField() + " annotated with @Inject is asking for a type other than SyncBeanManager. That's not currently supported." );
}
}
}
}
public ErraiIocTestRunner(Class<?> unitTestClass) throws InitializationError {
super( unitTestClass );
}
@Override
protected List<TestRule> getTestRules( final Object target ) {
List<TestRule> testRules = new ArrayList<TestRule>(super.getTestRules( target ));
try {
testRules.add( ErraiBeanManagementTestRule.createInTestClassloader( getTestClass(), target ) );
} catch ( Exception e ) {
throw new RuntimeException( e );
}
return testRules;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment