It's easy to write your own JUnit test-runner. A developer on the java-platform often writes code in the context of some framework or container platform (Spring, Play, java-EE, OSGi, Guice, whatever) that provides facilities for application bootstrap and dependency injection. When at some point she wants to write a JUnit test suite for some subset of code the developer is faced with the problem of how to recreate the application framework's runtime context within JUnit's runtime context - which assumes a simple no-argument constructor for test-harness classes.
Fortunately - JUnit provides a serviceable solution for augmenting the test suite runtime context in the form of test runners. A developer uses an annotation on his test class to indicate that the tests require a custom runner. For example - Spring provides its SpringJUnit4ClassRunner which allows a test developer to use Spring annotations in her test suties - like this:
@RunWith(SpringJUnit4ClassRunner.class)
class WhateverTest {
@Service(name="whatever")
Whatever service;
@Test
public void testSomething() {
assertTrue( ... );
}
...
}
The Littleware project includes a system that extends Guice with a simple module and bootstrap mechanism that I was able to integrate with JUnit with a simple LittleTestRunner that follows the same trick that Spring's test runner uses of simply extending JUnit's default BlockJUnit4ClassRunner - overriding the createTest method - easy cheesey:
package littleware.test;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import littleware.bootstrap.LittleBootstrap;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;
/**
* JUnit4 test runner enabled with littleware.bootstrap Guice bootrap and
* injection. Imitation of SpringJunit4TestRunner:
*
*/
public class LittleTestRunner extends BlockJUnit4ClassRunner {
private static final Logger log = Logger.getLogger(LittleTestRunner.class.getName());
/**
* Disable BlockJUnit4ClassRunner test-class constructor rules
*/
@Override
protected void validateConstructor( List<Throwable> errors ) {}
/**
* Construct a new {@code LittleTestRunner} and initialize a
* {@link LittleBootstrap} to provide littleware testing functionality to
* standard JUnit tests.
*
* @param clazz the test class to be run
* @see #createTestContextManager(Class)
*/
public LittleTestRunner(Class<?> clazz) throws InitializationError {
super(clazz);
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, "constructor called with [{0}]", clazz);
}
}
/**
* This is where littleware hooks in
*
* @return an instance of getClass constructed via the littleware managed Guice injector
*/
@Override
protected Object createTest() {
try {
return LittleBootstrap.factory.lookup(this.getTestClass().getJavaClass());
} catch ( RuntimeException ex ) {
log.log( Level.SEVERE, "Test class construction failed", ex );
throw ex;
}
}
}
Now I can write tests in Guice's "inject dependencies into the constructor" style - like this:
/**
* Just run UUIDFactory implementations through a simple test
*/
@RunWith(LittleTestRunner.class)
public class UUIDFactoryTester {
private final Provider<UUID> uuidFactory;
/**
* Constructor stashes UUIDFactory to run test
* against
*/
@Inject()
public UUIDFactoryTester(Provider<UUID> uuidFactory) {
this.uuidFactory = uuidFactory;
}
/**
* Just get a couple UUID's, then go back and forth to the string
* representation
*/
@Test
public void testUUIDFactory() {
//...
}
}