Java Mockito-注入一个mock列表

Java Mockito-注入一个mock列表,java,spring,junit,dependency-injection,mockito,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" r

我有以下代码:

@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);
    } 
}
我想用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);
}
,表示“策略”列表已初始化,但为空。 莫希托有没有办法表现得像春天一样?是否自动将所有实现接口“策略”的实例添加到列表中


顺便说一句,我在包装类中没有任何setter,如果可能的话,我希望以这种方式保留它。

您不应该模拟集合

创建所需的模拟并将其放入列表:

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);
}
私有列表策略;//不要嘲笑!
@嘲弄
策略策略策略;
@嘲弄
战略b战略b;
@以前
公共作废设置(){
strategies=Arrays.asList(strategyA,strategyB);
testedObject.strategies=策略;
}
@试验
public void应返回NameCreditenId()
{//不可逆代码。。。
//什么时候
testedObject.getName(ID);
}

Mockito无法知道您想将某些内容放入列表中

你应该重新考虑这件事,做点像这样的事

@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);
}
@InjectMocks
私有包装testedObject=新包装();
私有列表策略;
@嘲弄
策略策略策略;
@嘲弄
战略b战略b;
@以前
public void setup()引发异常{
mockedStrategies=Arrays.asList(strategyA,strategyB);
包装器。设置策略(模拟策略);
}

为什么不模拟调用
toStream()

@InjectMocks
私有包装testedObject=新包装();
私有列表策略;
@嘲弄
策略策略策略;
@嘲弄
战略b战略b;
@以前
公共作废设置(){
when(strategies.stream()).thenReturn(stream.of(strategyA,strategyB));
}

对我来说,这要优雅得多,因为它不需要您添加仅与代码测试相关的助手方法。

用@Spy而不是@Mock注释它。由于Mockito无法监视接口,请使用具体的实现,例如ArrayList。 在测试设置期间,将模拟添加到spy列表中。 这样,您就不需要仅仅为了测试目的而更改测试主题

@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);
}
@InjectMocks
私有包装testedObject=新包装();
@间谍
私有ArrayList模拟策略;
@嘲弄
私人战略a战略a;
@嘲弄
私人战略b战略b;
@以前
public void setup()引发异常{
mockedStrategies.add(strategyA);
mockedStrategies.add(strategyB);
}

来自Erwin Dupont的解决方案很好,但当您需要将模拟列表注入到测试对象的构造函数中时,该解决方案不起作用

我就是这样度过的。我只展示了列表中1项的解决方案,但您可以通过将
开关(索引)
放入
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);
  }
}
类包装器{
私人最终名单战略;
包装器(列表策略){this.strategies=newarraylist(策略);}
// ...
}
类包装器测试{
@注射模拟
私有对象;
@间谍
私有列表mockedStrategies new AbstractList(){
@重写公共触发器get(int-index){return Trigger;}//可以在没有边界检查的情况下离开
@重写公共int size(){return 1;}
};
@嘲弄
私人战略;
@试验
公共空间{
assertThat(testedObject).isNotNull();
assertThat(testedObject.getStrategies()).hasSize(1);
}
}

Thomas,Spring不知怎么知道。。所以我想知道莫基托是否也有同样的想法。除此之外,我不想将“setStrategies”setter添加到包装类中。但我很确定Spring也不能连接通用列表。类型信息在运行时也不可用,因为由于类型删除阶段,编译器不会将其写入字节码。因此,我想如果不告诉/programmingspring可以连接示例“Wrapper”类,注入strategyA和strategyB bean,就不可能创建包含mock的mock列表。应用程序上下文仅基于注释构建,因此我希望Mockito也能这样做。Timothy,我希望得到一个解决方案,在这个解决方案中,我不必将“策略”列表显式地设置为“testedObject”。谢谢,对具体impl的解释正是我仍然缺少的谜题的一部分。。。我尝试了@Mock和call real方法answer,但是没有添加它们!有用,但在将列表注入目标对象后,这些项会添加到列表中。因此,如果您制作列表内容的防御性副本,此方法将失败。如果您采用此方法,则不希望将testedObject设置为
new Wrapper()
,因为
@InjectMocks
注释告诉Mockito为您实例化它;此外,模拟列表可以是一个简单的
列表
(不需要是具体的类
ArrayList
),实际上我尝试使用@Spy list,但它不起作用。它必须是
数组列表
。我认为这是因为spy将调用List上的real
add
方法,而该方法与接口中的List没有任何作用。
@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));
}
@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);
}
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);
  }
}