基于Spock交互的测试:方法调用太少

时间:2018-06-23 22:44:22

标签: unit-testing grails groovy spock

下面有一个非常简单的方法。它会调用另一种方法,该方法先删除API密钥,然后再调用另一种方法来创建新密钥并返回。

下面的测试也只是检查两个方法是否被正确调用。但是尽管在这两种方法上仍然会收到0调用错误。是什么原因导致这个问题?

AuthApiKeyPair updateApiKeyPair(AuthApiKeyPair apiKeyPair, Boolean createNewKey) {

    AuthApiKeyPair newKeyPair

    if (createNewKey) {
        deleteApiKeyPair(apiKeyPair)

        //The key will be created with the same info as the previous key.
        newKeyPair = createApiKeyPair(apiKeyPair.label, apiKeyPair.accountMode, apiKeyPair.source)
    }

    newKeyPair
}

测试:

def "should soft delete key pair and create new one"() {
    setup:
    AuthApiKeyPair apiKeyPair = AuthApiKeyPair.build(acquirerId: 123, source: PaymentSource.BOARDING_API, label: 'Boarding API key')

    when:
    service.updateApiKeyPair(apiKeyPair, true)

    then:
    1 * service.deleteApiKeyPair(apiKeyPair)
    1 * service.createApiKeyPair(apiKeyPair.label, apiKeyPair.accountMode, apiKeyPair.source)
}

1 个答案:

答案 0 :(得分:4)

如果考虑测试,您将意识到,在最佳情况下,它会测试Spock的模拟机制,而不是您的业务代码。您尚未向我们展示完整的测试规范类,但是根据您的情况,我们可以假设测试中的service只是一个模拟。如果是这样,那么您就不能期望这两个调用:

then:
1 * service.deleteApiKeyPair(apiKeyPair)
1 * service.createApiKeyPair(apiKeyPair.label, apiKeyPair.accountMode, apiKeyPair.source)

发生。仅仅是因为模拟类不能执行真实的方法。

我强烈建议您测试一个真实的类,而不是测试特定方法导致哪种类型的调用,而是调用特定方法的预期(确定性)结果是什么。您可以在service.updateApiKeyPair(apiKeyPair, true)子句中的真实对象上执行when,然后可以检查是否创建了新的API密钥对(并持久保存在您使用的存储中),以及旧的对密钥是否已不存在。与仅检查调用相比,这种测试至少具有一些好处:

  • 您可以随时更改service.updateApiKeyPair()的实现,只要它产生相同的结果,您的测试仍然有用(因为该测试不会像调用测试那样限制您的实现),
  • 您测试实际行为,而不是模拟库-有这个 轶事说Mockito是数以百万计的项目中有史以来最受测试的库。

当然,可能需要进行一些设计更改。我猜想您的服务类使用一些注入的DAO或存储库,这些存储库会保留API密钥对。考虑为测试提供这种DAO的内存实现-一个类,而不是将对象持久存储在真实数据库中,而是将所有对象存储在内部ConcurrentMap<K,V>中。因此,您仍然可以将测试作为单元测试运行,并且可以测试将createNewKey参数设置为true的API密钥对是否完全符合您的期望。或者,您可以编写一个替换H2数据库的集成测试,但这只会使您的测试引导时间更长。选择是您的。

有一条值得记住的规则-如果您的类/组件/功能等难以进行单元测试,则意味着选择了错误的设计。

替代:Spock的Spy对象

最后我故意提到一件事。 Spock支持所谓的“间谍”对象,其行为类似于真实的对象,但是它们使您可以存根某些零件并将其作为模拟对象对待,例如。调用计数。但是,即使Spock的作者也警告开发人员使用此功能:

  

(使用此功能之前请三思。更改规范下的代码设计可能会更好。)

     

来源:http://spockframework.org/spock/docs/1.0/interaction_based_testing.html#spies

我不知道Grails是否具有用于创建Spy而不是Mock的测试的注释,但是您始终可以follow official documentation并创建普通的Spock单元测试以将您的服务实例化为Spy,然后您就可以尝试计算调用次数。我不建议这样做,只是为了记录而提及。