Mockito - Injecting a List of mocks

后端 未结 5 788
庸人自扰
庸人自扰 2020-12-31 00:50

I have the following code:

@Component 
public class Wrapper
{ 
    @Resource 
    private List strategies;

    public String getName(String          


        
相关标签:
5条回答
  • 2020-12-31 00:58

    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);
    }
    
    0 讨论(0)
  • 2020-12-31 01:04

    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);
    }
    
    0 讨论(0)
  • 2020-12-31 01:04

    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);
    }
    
    0 讨论(0)
  • 2020-12-31 01:11

    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.

    0 讨论(0)
  • 2020-12-31 01:14

    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);
      }
    }
    
    0 讨论(0)
提交回复
热议问题