我有以下代码:
@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课程中没有任何设置器,如果可能,我想以这种方式保留它。
答案 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));
}
对我而言,这更加优雅,因为它不需要您添加仅与测试代码相关的辅助方法。