Created
March 13, 2014 18:25
-
-
Save jfuerth/9533949 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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