Last active
December 19, 2015 06:09
-
-
Save raphaeljolivet/5909717 to your computer and use it in GitHub Desktop.
Junit Test Runner that automagically stubs Spring's @Autowired fields (setters) with Mockito mockups.
This file contains hidden or 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
/** | |
* Smart JunitRunner that autowires fields together for @Autowire (Spring) fields, | |
* or create empty mock otherwize. | |
* The properties marked with @AutoMock will be autwired with other properties | |
* marked with @AutoMock or new empty mock. | |
*/ | |
public class AutoMockTestRunner extends BlockJUnit4ClassRunner { | |
@Retention(RUNTIME) | |
@Target(FIELD) | |
static public @interface AutoMock { | |
}; | |
private Logger logger = LoggerFactory.getLogger(AutoMockTestRunner.class); | |
/** Map of avaiable mocks + objects to mock */ | |
private Map<Class, Object> instances = newHashMap(); | |
public AutoMockTestRunner(Class<?> klass) throws InitializationError { | |
super(klass); | |
} | |
@Override | |
protected Object createTest() throws Exception { | |
Object testClassInstance = super.createTest(); | |
Map<Field, Object> fields = newHashMap(); | |
// Get all fields annotated with @AutoMock | |
for (Field field : testClassInstance.getClass().getDeclaredFields()) { | |
if (field.getAnnotation(AutoMock.class) == null) { | |
continue; | |
} | |
field.setAccessible(true); | |
Object instance = field.get(testClassInstance); | |
if (instance == null) { | |
throw new RuntimeException(format("The field #%s is null : Cannot autowire", field.getName())); | |
} | |
fields.put(field, instance); | |
instances.put(instance.getClass(), instance); | |
} | |
for (Entry<Field, Object> field : fields.entrySet()) { | |
logger.info("Autowiring #{}", field.getKey().getName()); | |
autoMockInstance(field.getValue()); | |
} | |
return testClassInstance; | |
} | |
/** Look for @autowire annotations and either : Wire with available instance, or create new empty mock */ | |
private void autoMockInstance(Object autoMockable) { | |
// Look for @Autowire setters | |
for (Method setMethod : autoMockable.getClass().getMethods()) { | |
if (!setMethod.getName().startsWith("set")) { | |
continue; | |
} | |
if (setMethod.getAnnotation(Autowired.class) == null) { | |
continue; | |
} | |
Class setClass = setMethod.getParameterTypes()[0]; | |
Object instance = searchInstance(setClass); | |
logger.info("Autoriwing field {}", setMethod.getName()); | |
// Let's mock ! | |
if (instance == null) { | |
logger.info("Class {} not found => mocking", setClass.getSimpleName()); | |
instance = mock(setClass); | |
instances.put(setClass, instance); | |
} | |
// Wire it | |
try { | |
setMethod.invoke(autoMockable, instance); | |
} | |
catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
} | |
/** Search instance with compatible type */ | |
private Object searchInstance(Class clazz) { | |
for (Object instance : instances.values()) { | |
if (clazz.isInstance(instance)) { | |
return instance; | |
} | |
} | |
return null; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This snippnet is inspired from @InjectMocks, but more specifically designed to work with Spring's @Autowired.
Usage :
The fields of the test class annotated with @automock will have their @Autowired fields injected with :
If you want to define custom behavior for mocked up objects, you have to declare them manually in order to reference them in the @before method.