Mockito - 注入嘲笑列表

时间:2017-02-20 17:44:08

标签: java spring junit dependency-injection mockito

我有以下代码:

@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{...}

我想使用Mockito为它创建一个测试。 我把测试写成如下:

@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);
}

我在线上获得NullPointerException:

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

,其中声明“策略”列表已初始化但为空。 有没有什么方法Mohito会像春天一样表现出来?将实现“策略”界面的所有实例自动添加到列表中?

顺便说一句,我在Wrapper课程中没有任何设置器,如果可能,我想以这种方式保留它。

5 个答案:

答案 0 :(得分:10)

使用@Spy而不是@Mock注释它。由于Mockito无法监视接口,因此请使用具体实现,例如ArrayList。 在测试设置期间,将模拟添加到List间谍。 这样,您无需仅为测试目的更改测试对象。<​​/ p>

@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);
}

答案 1 :(得分:1)

你不应该模仿集合。

创建您需要的模拟并将它们放入列表中:

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);
}

答案 2 :(得分:1)

Mockito无法知道你想在List 策略中放入一些东西。

你应该重新考虑一下这样做

@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);
}

答案 3 :(得分:1)

Erwin Dupont提供的解决方案很好,但是当您需要在测试对象的构造函数中插入模拟列表时,该解决方案将无法正常工作。

这就是我的解决方法。我只显示了列表中1个项目的解决方案,但是您可以通过将switch(index)放入get()方法中来将其扩展到N个项目:

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);
  }
}

答案 4 :(得分:0)

为什么不嘲笑你对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));
}

对我而言,这更加优雅,因为它不需要您添加仅与测试代码相关的辅助方法。