Skip to content

Instantly share code, notes, and snippets.

@keerts
Created March 12, 2012 09:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save keerts/2020820 to your computer and use it in GitHub Desktop.
Save keerts/2020820 to your computer and use it in GitHub Desktop.
Flexible methods in JUnit
package nl.avisi.langur.test;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
public class InputProvidingRunner extends BlockJUnit4ClassRunner {
public InputProvidingRunner(Class<?> klass) throws InitializationError {
super(klass);
}
@Override
protected void validateTestMethods(List<Throwable> errors) {
validatePublicVoidAndSuitableMethodsArgs(Test.class, false, errors);
}
protected void validatePublicVoidAndSuitableMethodsArgs(Class<? extends Annotation> annotation, boolean isStatic, List<Throwable> errors) {
List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(annotation);
for (FrameworkMethod eachTestMethod : methods)
validatePublicVoidAndSuitableMethodArgs(eachTestMethod, isStatic, errors);
}
public void validatePublicVoidAndSuitableMethodArgs(FrameworkMethod testMethod, boolean isStatic, List<Throwable> errors) {
testMethod.validatePublicVoid(isStatic, errors);
validateSuitableMethodArgs(testMethod, errors);
}
public void validateSuitableMethodArgs(FrameworkMethod fMethod, List<Throwable> errors) {
Method javaMethod = fMethod.getMethod();
Annotation[][] parameterAnnotations = javaMethod.getParameterAnnotations();
for (Annotation[] annotations : parameterAnnotations) {
if (annotations.length != 1) {
errors.add(new Exception("Method " + fMethod.getName() + " should have exactly 1 annotation"));
}
if (annotations[0].annotationType() != TestInput.class) {
errors.add(new Exception("Method " + fMethod.getName() + " parameters can only be annotated with @Testinput"));
}
}
}
@Override
protected Statement methodInvoker(final FrameworkMethod fMethod, final Object test) {
Method javaMethod = fMethod.getMethod();
Annotation[][] parameterAnnotations = javaMethod.getParameterAnnotations();
final Object[] params = new Object[parameterAnnotations.length];
for (int i = 0; i < params.length; i++) {
Annotation[] annotations = parameterAnnotations[i];
TestInput testInput = (TestInput) annotations[0];
String location = testInput.value();
Class<?> parameterType = javaMethod.getParameterTypes()[i];
params[i] = injectResource(location, parameterType);
}
return new Statement() {
@Override
public void evaluate() throws Throwable {
fMethod.invokeExplosively(test, params);
}
};
}
private Object injectResource(String location, Class<?> parameterType) {
Resource resource;
try {
// spring dependency, with a bit of work, you can easily get around it
resource = new DefaultResourceLoader().getResource(location);
if (parameterType == InputStream.class) {
return resource.getInputStream();
} else if (parameterType == Reader.class) {
return new InputStreamReader(resource.getInputStream());
} else if (parameterType == String.class) {
// commons.io dependency. If you want, you can easily get rid of this dependency
return IOUtils.toString(resource.getInputStream());
}
} catch (Exception e) {
throw new TestInputNotFoundException(e);
}
throw new TestInputNotFoundException("unsupported resource type, use one of [Reader, InputStream, String]");
}
}
package nl.avisi.langur.test;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(value = { ElementType.PARAMETER })
@Retention(RUNTIME)
public @interface TestInput {
String value();
}
package nl.avisi.langur.test;
public class TestInputNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
public TestInputNotFoundException(Exception e) {
super(e);
}
public TestInputNotFoundException(String message) {
super(message);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment