验证使用Mockito调用函数的次数有什么意义?

sco*_*ott 15 java testing unit-testing mockito

在我的理解中,代码测试是测试结果是否正确,如计算器,我需要编写一个测试用例来验证1 + 1的结果是否为2.

但是我已经阅读了很多关于验证方法调用次数的测试用例.我很困惑.最好的例子就是我在Spring in Action中看到的:

public class BraveKnight implements Knight {
    private Quest quest;
    public BraveKnight(Quest quest) { 
        this.quest = quest; 
    }
    public void embarkOnQuest() {
        quest.embark(); 
    }
}

public class BraveKnightTest {
    @Test 
    public void knightShouldEmbarkOnQuest() { 
        Quest mockQuest = mock(Quest.class); 
        BraveKnight knight = new BraveKnight(mockQuest); 
        knight.embarkOnQuest(); 
        verify(mockQuest, times(1)).embark(); 
    }
}
Run Code Online (Sandbox Code Playgroud)

我真的不知道为什么他们需要验证embark()函数被调用一次.难道你不认为embark()在调用后肯定会被调用embarkOnQuest()吗?或者会发生一些错误,我会在日志中看到错误消息,显示错误行号,可以帮助我快速找到错误的代码.

那么如上所述验证的重点是什么?

ern*_*t_k 9

需要很简单:验证是否进行了正确的调用次数.在某些情况下,方法调用不应该发生,而其他情况下,它们应该发生多于或少于默认值.

考虑以下修改版本embarkOnQuest:

public void embarkOnQuest() {
    quest.embark(); 
    quest.embarkAgain(); 
}
Run Code Online (Sandbox Code Playgroud)

并假设您正在测试以下错误案例quest.embark():

@Test 
public void knightShouldEmbarkOnQuest() { 
    Quest mockQuest = mock(Quest.class); 
    Mockito.doThrow(RuntimeException.class).when(mockQuest).embark();
    ...
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您要确保quest.embarkAgain未调用(或调用0次):

verify(mockQuest, times(0)).embarkAgain(); //or verifyZeroInteractions
Run Code Online (Sandbox Code Playgroud)

当然这是另一个简单的例子.还可以添加许多其他示例:

  • 应该在第一次获取时缓存条目的数据库连接器,可以进行多次调用并验证与数据库的连接只被调用一次(每个测试查询)
  • 在加载(或懒惰)上进行初始化的单例对象,可以测试与初始化相关的调用只进行一次.


JET*_*ETM 5

好问题.你提出了一个很好的观点,即当你可以检查结果时,模拟可能会过于迂回.但是,有一些情况会导致更强大的测试.

例如,如果某个方法需要调用外部API,则只需测试结果就会出现一些问题:

  • 网络I/O很慢.如果您有这样的许多检查,它将减慢您的测试用例
  • 像这样的任何往返都必须依赖于代码,使得请求,API和解释API响应的代码都能正常工作.这是单个测试的很多失败点.
  • 如果发生了一些愚蠢的事情并且您不小心发出了多个请求,这可能会导致您的程序出现性能问题.

解决您的子问题:

难道你不认为在调用embarkOnQuest()之后会调用embark()吗?

测试也有助于你重构而不用担心破坏事物.这是显而易见的,是的.这会在6个月内显而易见吗?

  • 重构正是为了改变实现,所以可能在不调用`embark()`的情况下得到结果.专注于验证代码调用该方法而不是结果在规范内的测试是没有意义的.如果您想要克服慢速网络I/O,您应该模拟它以提供预定义的响应,然后测试被测方法是否使用该模拟响应产生正确的结果.测试的关键是结果,而不是特定的方法是否被调用. (3认同)

Ben*_*een 5

请考虑以下代码:

public void saveFooIfFlagTrue(Foo foo, boolean flag) {
    if (flag) {
        fooRepository.save(foo);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您不检查fooRepository.save()调用的次数,那么您如何知道此方法是否正在执行您想要的操作?

这适用于其他void方法.如果没有返回方法,因此没有响应验证,则检查调用哪些其他方法是验证方法行为正确的好方法.