Search code examples
javagenericsjunitassertthat

How to make JUnit assertThat() work with lower bounded wildcard?


initially, I had this piece of production code:

interface ActionSequence {
   public List<Actions> getActions();

I tested classes implementing that interface with something like this:

assertThat(sequenceUnderTest.getActions(), is(Arrays.asList(action1, action2));

Then I figured that it might be beneficial to change the production interface to:

public List<? extends Action> getActions() {

(to allow me to return lists of subclasses of Action).

But now eclipse tells me:

The method assertThat(T, Matcher<? super T>) in the type Assert is not applicable for the arguments (List<capture#1-of ? extends Action>, Matcher<List<Action>>)

I figured: when I change the class that implements ActionInterface to do

@Override
public List<SomeSubClassOfAction> getActions()

(instead of keeping the wildcard) ... then everything works. But why?


Solution

  • Arrays.asList(action1, action2) will return a List<Action>. is(Arrays.asList(action1, action2)) will therefore return a Matcher<List<Action>>.

    assertThat has the following signature:

    assertThat(T actual, Matcher<T> matcher)
    

    So assertThat requires the following parameters in your case:

    assertThat(List<Action>, Matcher<List<Action>>)
    

    But your first parameter is a List<? extends Action> And a List<Action> is completely different from a List<? extends Action>. For example, you cannot put Action elements into a List<SomeSubClassOfAction>. This is why this won't work.

    For details, see Angelika Langer's excellent site: http://www.angelikalanger.com/GenericsFAQ/FAQSections/Index.html