gradle Spock的单元测试调用太少

p3rjfoxz  于 2023-04-06  发布在  其他
关注(0)|答案(1)|浏览(153)

第一次使用Spock(对Groovy的了解有限),我怀疑我正在使用Spock的"one giant gotcha"
对同一个方法调用的模拟和存根必须发生在同一个交互中。
但是我不太明白我的例子如何匹配其他问题(例如123)-所有这些都涉及返回值的方法,其中所讨论的方法在given代码中被模拟,然后在then中被验证。我的代码正在验证void方法。
这是我正在测试的代码:

package com.company

import app.cash.licensee.LicenseeExtension
import app.cash.licensee.LicenseePlugin
import org.gradle.api.Plugin
import org.gradle.api.Project

class ThirdPartyLicensesCheckPlugin implements Plugin<Project> {

    @Override
    void apply(final Project project) {
        project.pluginManager.apply(LicenseePlugin)

        def licenseeExtension = project.extensions.findByType(LicenseeExtension)
        project.pluginManager.withPlugin("app.cash.licensee") {
            licenseeExtension.ignoreDependencies("com.company", "common-utils") {
                because "internal project"
            }
        }
    }
}

我想验证许可证插件的应用和配置是否正确。
这是我的测试代码:

package com.company

import app.cash.licensee.LicenseeExtension
import app.cash.licensee.LicenseePlugin
import org.gradle.api.Project
import org.gradle.api.plugins.ExtensionContainer
import org.gradle.api.plugins.PluginManager
import spock.lang.Specification

class ThirdPartyLicensesCheckPluginTest extends Specification {

    private ThirdPartyLicensesCheckPlugin plugin

    // Class dependencies

    // Mocks for tests
    private Project project
    private PluginManager pluginManager
    private LicenseeExtension licenseeExtension

    def setup() {
        plugin = new ThirdPartyLicensesCheckPlugin()
        project = Mock(Project)
        pluginManager = Mock(PluginManager)
        project.pluginManager >> pluginManager

        licenseeExtension = Mock(LicenseeExtension)
        project.extensions >> Mock(ExtensionContainer) {
            findByType(LicenseeExtension) >> licenseeExtension
        }
    }

    def "the cashapp/licensee plugin is applied to the project"() {
        given: "a Gradle project"

        when: "this plugin is applied"
        plugin.apply(project)

        then: "the cashapp/licensee plugin is applied"
        1 * pluginManager.apply(LicenseePlugin)
        1 * pluginManager.withPlugin("app.cash.licensee", _)
        1 * licenseeExtension.ignoreDependencies("com.company", "common-utils") { because("internal project")}
    }
}

我遇到的失败是:

Too few invocations for:

1 * licenseeExtension.ignoreDependencies("com.company", "common-utils") { because("internal project")}   (0 invocations)

Unmatched invocations (ordered by similarity):

None

我尝试在检查withPlugin的同时验证action参数,如下所示:

package com.company

import app.cash.licensee.LicenseeExtension
import app.cash.licensee.LicenseePlugin
import org.gradle.api.Project
import org.gradle.api.plugins.ExtensionContainer
import org.gradle.api.plugins.PluginManager
import spock.lang.Specification

class ThirdPartyLicensesCheckPluginTest extends Specification {

    private ThirdPartyLicensesCheckPlugin plugin

    // Class dependencies

    // Mocks for tests
    private Project project
    private PluginManager pluginManager
    private LicenseeExtension licenseeExtension

    def setup() {
        plugin = new ThirdPartyLicensesCheckPlugin()
        project = Mock(Project)
        pluginManager = Mock(PluginManager)
        project.pluginManager >> pluginManager

        licenseeExtension = Mock(LicenseeExtension)
        project.extensions >> Mock(ExtensionContainer) {
            findByType(LicenseeExtension) >> licenseeExtension
        }
    }

    def "the cashapp/licensee plugin is applied to the project"() {
        given: "a Gradle project"

        when: "this plugin is applied"
        plugin.apply(project)

        then: "the cashapp/licensee plugin is applied"
        1 * pluginManager.apply(LicenseePlugin)
        1 * pluginManager.withPlugin("app.cash.licensee") {
            licenseeExtension.ignoreDependencies("wrong.group", "incorrect-project") { because("the wrong reason")}
        }
    }
}

但这总是通过,即使验证是错误的。

osh3o9ms

osh3o9ms1#

虽然我同意@tim_yates的观点,您可能希望使用集成测试,但您的问题是您没有调用闭包。
下面是一个简化版本:

import spock.lang.*

class ASpec extends Specification {
  def "hello world"() {
      given:
      List listMock = Mock()
      PluginContainer pluginContainer = Mock()
      
      when:
      pluginContainer.withPlugin('123') {
          listMock.add(42)
      }    
      then: 
      1 * pluginContainer.withPlugin('123', _) >> { it[1].call() }
      1 * listMock.add(42)
  }
}

interface PluginContainer {
    withPlugin(String id, Closure closure)
}

Groovy Web Console
你不能Assert闭包是这样传递的,它们不是简单的值。
你需要模仿它,例如像这样:

import spock.lang.*

class ASpec extends Specification {
  def "hello world"() {
      given:
      Reason reasonMock = Mock()
      PluginContainer pluginContainer = Mock()
      
      when:
      pluginContainer.withPlugin('123') {
          because("The answer is 42")
      }    
      then: 
      1 * pluginContainer.withPlugin('123', _) >> { reasonMock.with(it[1]) }
      1 * reasonMock.because("The answer is 42")
  }
}

interface PluginContainer {
    withPlugin(String id, Closure closure)
}

interface Reason {
    void because(String reason)
}

Groovy Web Console
1 * pluginContainer.withPlugin('123', _) >> { reasonMock.with(it[1]) }将调用闭包,reasonMock被设置为delegate

相关问题