【问题标题】:Getting a InvalidUseOfMatchersException when stubbing a method存根方法时获取 InvalidUseOfMatchersException
【发布时间】:2014-02-12 19:59:30
【问题描述】:

我有这个TestNG测试方法代码:

@InjectMocks
private FilmeService filmeService = new FilmeServiceImpl();

@Mock
private FilmeDAO filmeDao;

@BeforeMethod(alwaysRun=true)
public void injectDao() {
    MockitoAnnotations.initMocks(this);
}

//... another tests here

@Test
public void getRandomEnqueteFilmes() {
    @SuppressWarnings("unchecked")
    List<Filme> listaFilmes = mock(List.class);

    when(listaFilmes.get(anyInt())).thenReturn(any(Filme.class));
    when(filmeDao.listAll()).thenReturn(listaFilmes);

    List<Filme> filmes = filmeService.getRandomEnqueteFilmes();

    assertNotNull(filmes, "Lista de filmes retornou vazia");
    assertEquals(filmes.size(), 2, "Lista não retornou com 2 filmes");
}

我得到一个“org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 参数匹配器的使用无效! 预期 0 个匹配器,1 个记录:”在此代码中调用 listAll() 方法:

@Override
public List<Filme> getRandomEnqueteFilmes() {
    int indice1, indice2 = 0;
    List<Filme> filmesExibir = new ArrayList<Filme>();
    List<Filme> filmes = dao.listAll();

    Random randomGenerator = new Random();
    indice1 = randomGenerator.nextInt(5);
    do {
        indice2 = randomGenerator.nextInt(5);
    } while(indice1 == indice2);

    filmesExibir.add(filmes.get(indice1));
    filmesExibir.add(filmes.get(indice2));

    return filmesExibir;
}

我很确定我在这里遗漏了一些东西,但我不知道它是什么!有人帮忙吗?

【问题讨论】:

  • dao.listAll() 方法是最终的吗? dao 是什么类型?您可能遇到与this question 相同的问题。
  • @andersschuller 不,dao 是被filmeDao 模拟的对象,我已经改进了我的问题以使其更加明显。我已经阅读了您提到的问题,但没有任何内容可以澄清我的问题的答案。

标签: java tdd testng mockito


【解决方案1】:
when(listaFilmes.get(anyInt())).thenReturn(any(Filme.class));

你的问题。您不能在返回值中使用anyany 是一个匹配器——它用于匹配参数值以进行存根和验证——在定义调用的返回值时没有意义。您需要显式返回一个 Filme 实例,或者将其保留为 null(这是默认行为,会破坏存根点)。

我应该注意,使用真实列表而不是模拟列表通常是个好主意。与您开发的自定义代码不同,List 实现定义良好且经过良好测试,并且与模拟 List 不同,如果您重构被测系统以调用不同的方法,则真正的 List 不太可能破坏。这是风格和测试理念的问题,但您可能会发现在此处使用真正的 List 是有利的。


为什么上述规则会导致该异常?好吧,这个解释打破了 Mockito 的一些抽象,但是 matchers don't behave like you think they might——它们将一个值记录到 ArgumentMatcher 对象的秘密 ThreadLocal 堆栈中,并返回 null 或其他一些虚拟值,并在对 whenverify 的调用中Mockito 看到一个非空堆栈,并且知道优先使用这些匹配器而不是实际参数值。就 Mockito 和 Java 评估顺序而言,您的代码如下所示:

when(listaFilmes.get(anyInt())).thenReturn(null);
when(filmeDao.listAll(any())).thenReturn(listaFilmes); // nonsense

Mockito 自然会看到any 匹配器,而listAll 不接受参数,因此预期有0 个匹配器,记录了1 个

【讨论】:

  • 是的,伙计!你明白了!......我知道我错过了一些东西,而你刚刚找到了它。尽管我在“模拟列表”问题中不完全同意您的看法,因为我不想测试列表本身,而是另一个使用它的代码,我不想提供此列表,因为这可能会成为在列表可能会变大的地方很难做到。
  • 很高兴为您提供帮助!关于列表,这是一个哲学问题,Martin Fowler 在他的有用文章"Mocks Aren't Stubs" 中写到了这个问题。 (整本书都值得一读。)当然,欢迎您继续使用模拟(理性的人不同意这里),但请注意,如果您重构代码以使用不同的 List 方法,它可能会破坏您的测试,记住这一点总是很好您可以在此处选择其他选项(存根、假货和真实对象)。干杯!
  • 是的,我和 Jeff(和 Martin Fowler)一起讨论这个问题。任何列表都是一个值对象。它没有值得存根或删除的“真正”功能,因此模拟它从来没有任何意义。只需使用您希望它具有的任何值制作一个真实的列表,其中可能包括也可能不包括模拟。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多