使用Mockito,如何验证某个方法是否被调用了特定的参数?

75

我正在使用Mockito 1.9.0。如何验证一个方法正好被调用了一次,并且其中一个传递给它的字段包含特定的值?在我的JUnit测试中,我有:

@Before
public void setupMainProg() { 
    // Initialize m_orderSvc, m_opportunitySvc, m_myprojectOrgSvc
    ...
    m_prog = new ProcessOrdersWorker(m_orderSvc, m_opportunitySvc, m_myprojectOrgSvc);
}   // setupMainProg

@Test
public void testItAll() throws GeneralSecurityException, IOException { 
    m_prog.work();  
}

方法“work”调用了“m_orderSvc”的一个方法(它是传递给对象的参数之一)。"m_orderSvc"反过来包含一个成员字段"m_contractsDao"。我想验证"m_contractsDao.save"只被调用了一次,并且传递给它的参数包含特定值。

这可能有点令人困惑。请让我知道如何澄清我的问题,我很乐意这样做。

3个回答

75

首先,您需要创建一个模拟的m_contractsDao并进行设置。假设该类为ContractsDao:

ContractsDao mock_contractsDao = mock(ContractsDao.class);
when(mock_contractsDao.save(any(String.class))).thenReturn("Some result");

然后将模拟对象注入到m_orderSvc中并调用您的方法。

m_orderSvc.m_contractsDao = mock_contractsDao;
m_prog = new ProcessOrdersWorker(m_orderSvc, m_opportunitySvc, m_myprojectOrgSvc);
m_prog.work(); 

最后,验证模拟对象是否被正确调用:

verify(mock_contractsDao, times(1)).save("Parameter I'm expecting");

10
请注意,除非您添加特定于一次以外的量词,否则可以省略, times(1),因为它始终隐含着一次。而且,any(String.class) 可以替换为稍微更方便的 anyString() - Kevin Welker
3
值得注意的是,在verify方法之后传递给方法的参数使用equals方法与实际测试期间传递的参数进行比较。因此,无论该方法是什么(例如mamboking示例中的save方法),都要考虑每个参数的类型以及是否真正需要使用equals进行比较。如果您希望使用除equals以外的内容对参数进行测试,则需要某种类型的ArgumentMatcher(可能是Kevin Welker回答中的ArgumentCaptor)。 - Dawood ibn Kareem
你如何明确指定只有一次,而不是两次或更多次?@KevinWelker的评论说这是隐含的,但不确定它是否意味着只有一次,还是至少一次。 - aliteralmind
1
有没有一种方法,可以传递一个谓词来代替我期望的确切参数?例如:verify(mock_contractsDao, times(1)).save((String s) -> s.length() == 23); - jameshfisher
1
我还没有尝试过 Mockito 2.x 中的任何内容,但您可能会在新的 ArgumentMatcher 中找到所需的内容。这个语法可能不对,但是在 Mockito 2.x 下,以下代码可能接近您想要的:verify(mock_contractsDao).save(argThat(s->s.length==23)); - Kevin Welker
显示剩余3条评论

30

基于Mamboking的回答:

ContractsDao mock_contractsDao = mock(ContractsDao.class);
when(mock_contractsDao.save(anyString())).thenReturn("Some result");

m_orderSvc.m_contractsDao = mock_contractsDao;
m_prog = new ProcessOrdersWorker(m_orderSvc, m_opportunitySvc, m_myprojectOrgSvc);
m_prog.work(); 

针对你的请求验证参数是否包含某个值,我可以假设你的参数是一个字符串,并且你想测试该字符串参数是否包含一个子串。为此,你可以执行以下操作:

ArgumentCaptor<String> savedCaptor = ArgumentCaptor.forClass(String.class);
verify(mock_contractsDao).save(savedCaptor.capture());
assertTrue(savedCaptor.getValue().contains("substring I want to find");

如果这样的假设是错误的,save()的参数是某种集合,那么只有略微的区别:

ArgumentCaptor<Collection<MyType>> savedCaptor = ArgumentCaptor.forClass(Collection.class);
verify(mock_contractsDao).save(savedCaptor.capture());
assertTrue(savedCaptor.getValue().contains(someMyTypeElementToFindInCollection);

如果您知道如何使用Hamcrest匹配器,还可以查看ArgumentMatchers


20

这是更好的解决方案:

verify(mock_contractsDao, times(1)).save(Mockito.eq("Parameter I'm expecting"));

当你处理非原始类型时,这会更好。 - Pikachu
这也可以工作,如果您有其他匹配器(如any(),anyOrNull()等),则必须使用eq()。您可以同时使用匹配器和字面量。 - Amir Ziarati

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接