Mockito - Injecting a List of mocks

19,968

Solution 1

Mockito can not know that you want to put somthing in the List strategies.

You should rethink this an do something like this

@InjectMocks
private Wrapper testedObject = new Wrapper ();

private List<Strategy> mockedStrategies;

@Mock
StrategyA strategyA;

@Mock
StrategyB strategyB;

@Before
public void setup() throws Exception {
    mockedStrategies = Arrays.asList(strategyA, strategyB);
    wrapper.setStrategies(mockedStrategies);
}

Solution 2

Annotate it with @Spy instead of @Mock. As Mockito cannot spy on an interface, use a concrete implementation, for example ArrayList. During test setup add the mocks to the List spy. This way you do not need to alter your test subject solely for test purposes.

@InjectMocks
private Wrapper testedObject = new Wrapper();

@Spy
private ArrayList<Strategy> mockedStrategies;

@Mock
private StrategyA strategyA;

@Mock
private StrategyB strategyB;

@Before
public void setup() throws Exception {
    mockedStrategies.add(strategyA);
    mockedStrategies.add(strategyB);
}

Solution 3

You should not mock collections.

Create the mocks you need and put them into a list:

private List<Strategy> strategies; // not mocked!

@Mock
StrategyA strategyA;

@Mock
StrategyB strategyB;

@Before
public void setup(){
  strategies= Arrays.asList(strategyA,strategyB);
  testedObject.strategies= strategies;
}

@Test
public void shouldReturnNameForGivenId()
{   // irrevelant code...
    //when
    testedObject.getName(ID);
}

Solution 4

Why not just mock out your call to toStream()?

@InjectMocks
private Wrapper testedObject = new Wrapper();

private List<Strategy> mockedStrategies;

@Mock
StrategyA strategyA;

@Mock
StrategyB strategyB;

@Before
public void setup() {
    when(strategies.stream()).thenReturn(Stream.of(strategyA, strategyB));
}

To me this is far more elegant as it doesn't require you to add a helper method that is only relevant to testing to your code.

Solution 5

The solution from Erwin Dupont is nice but does not work when you need to inject the List of mocks in the constructor of the tested object.

Here's how I got round that. I've shown the solution for just 1 item in the list, but you could extend it to N items by putting a switch(index) into the get() method:

class Wrapper {
  private final List<Strategy> strategies;
  Wrapper(List<Strategy> strategies) { this.strategies = new ArrayList<>(strategies); }
  // ...
}

class WrapperTest {
  @InjectMocks
  private Wrapper testedObject;

  @Spy
  private List<Strategy> mockedStrategies new AbstractList<Strategy>() {
      @Override public Trigger get(int index) { return trigger; } // can get away without bounds-checking
      @Override public int size() { return 1; }
  };

  @Mock
  private Strategy strategy;

  @Test
  public void testSomething() {
    assertThat(testedObject).isNotNull();
    assertThat(testedObject.getStrategies()).hasSize(1);
  }
}
Share:
19,968
fascynacja
Author by

fascynacja

Updated on June 20, 2022

Comments

  • fascynacja
    fascynacja almost 2 years

    I have the following code:

    @Component 
    public class Wrapper
    { 
        @Resource 
        private List<Strategy> strategies;
    
        public String getName(String id)
        {
        // the revelant part of this statement is that I would like to iterate over "strategies"
            return strategies.stream()
                .filter(strategy -> strategy.isApplicable(id))
                .findFirst().get().getAmount(id);
        } 
    }
    

    @Component 
    public class StrategyA implements Strategy{...}
    
    @Component 
    public class StrategyB implements Strategy{...}
    

    I would like to create a Test for it using Mockito. I wrote the test as follows:

    @InjectMocks
    private Wrapper testedObject = new Wrapper ();
    
    // I was hoping that this list will contain both strategies: strategyA and strategyB
    @Mock
    private List<Strategy> strategies;
    
    @Mock
    StrategyA strategyA;
    
    @Mock
    StrategyB strategyB;
    
    @Test
    public void shouldReturnNameForGivenId()
    {   // irrevelant code...
        //when
        testedObject.getName(ID);
    }
    

    I am getting NullPointerException on line:

    filter(strategy -> strategy.isApplicable(id))
    

    , which states that the "strategies" list is initialized but is empty. Is there any way Mohito will behave in the same wasy as Spring? Adding automatically all instances implementing interface "Strategy" to the list?

    Btw I do not have any setters in Wrapper class and I would like to leave it in that way if possible.