3

In JUnit, I am using ParameterResolver to dynamically create and inject method argument to a Test, this works for non generic methods like the one below which is expecting Person peron. How can I instead change it to List<Person> personListand have the supportsParameter check if List is of Person

@ExtendWith({AnnotationProcessor.class})
public void testInject(Person person) {
    System.out.println(person);
}

AnnotationProcessor.java

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
        throws ParameterResolutionException {
    Parameter parameter = parameterContext.getParameter();

    return Person.class.equals(parameter.getType());
}

The closest I was able to use is :

   @Override
    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
            throws ParameterResolutionException {
        Parameter parameter = parameterContext.getParameter();

        if (parameter.getParameterizedType()!= null && List.class.equals(parameter.getType())) {
            if (parameter.getParameterizedType() instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType)parameter.getParameterizedType();
                Class<?> clazz = (Class<?>)pt.getActualTypeArguments()[0];
                if (clazz == Person.class) return true;
            }


        }

        return false;
    }
Novice User
  • 2,953
  • 4
  • 24
  • 40

1 Answers1

2

Here's the annotation processor that does what you want. Your attempt was pretty close I'd say.

class AnnotationProcessor implements ParameterResolver{
    @Override
    public boolean supportsParameter(
        ParameterContext parameterContext, 
        ExtensionContext extensionContext
    ) {
        Parameter parameter = parameterContext.getParameter();

        Type type = parameter.getParameterizedType();
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            if (!parameterizedType.getRawType().equals(List.class))
                return false;
            Type firstParameterType = parameterizedType.getActualTypeArguments()[0];
            return firstParameterType.equals(Person.class);
        }

        return false;
    }

    @Override
    public Object resolveParameter(
        ParameterContext parameterContext,
        ExtensionContext extensionContext
    ) {
        return Arrays.asList(new Person(), new Person());
    }
}

How does it work?

Handling annotations through reflection is an involved task because it was added to Java as an afterthought. That's why you have to do all the instanceof type checking. Apart from that it works reliably - I know because I use it in spades for http://jqwik.net.

Why does it work?

Type erasure does not strike in this case because type erasure only erases types from runtime objects. It does not erase types from type declarations like parameters and return types. If it did many of the funny things that libraries do with generic types would not work. There are a few bugs in the JDK regarding generic type handling together with annotations but they'll only get you if you go for esoteric stuff like "annotations on type parameters in innner non static classes". For straightforward uses like yours everything should be fine.

johanneslink
  • 2,761
  • 1
  • 17
  • 25