【问题标题】:Reiterate over Spock where block?重申Spock在哪里阻塞?
【发布时间】:2021-03-05 01:14:04
【问题描述】:

我们使用 Spock 放心进行 API 测试。我们有一种情况,我们正在从 CSV 文件中读取数据。所以对于这种情况,让我们假设我们正在读取 10 行数据。有没有一种方法可以针对给定数量的“公司”对这 10 行数据重复这个单一的测试?例如:

@Unroll
def "example test"() {
    when:
    //do something here

    then:
    //check something here

    where:
    row << functionalUtils.getRecordsFromCsv(csvFile)
}

无论 CSV 文件中有多少行,这都会运行测试,因此如果 CSV 文件中有 10 行,它将运行 10 次。

但是假设我们有 4 家我们支持的公司都拥有相同的数据,我们想要验证所有 4 家公司都是正确的。我们不想让 CSV 文件有 40 行(重复相同的 10 行 4 次),而是希望对 4 家公司重复这个测试 4 次。类似的东西

@Unroll
def "example test"() {
    when:
    //do something here

    then:
    //check something here

    where:
    company << ["company a","company b","company c","company d"]
    row << functionalUtils.getRecordsFromCsv(csvFile)
}

我让这件事变得更难了吗?这甚至可能吗?我知道这是不正确的,这会出错,我只是想证明我正在尝试做的事情。

【问题讨论】:

    标签: unit-testing testing groovy spock


    【解决方案1】:

    如果所有公司在 csv 文件中有相同的数据,您可以在 Spock 中使用 Groovy 的 combinations()multi variable data pipes

    where:
    [row, company] << [
        functionalUtils.getRecordsFromCsv(csvFile),
        ["company a","company b","company c","company d"]
    ].combinations()
    

    【讨论】:

    • 看来我需要升级到 Spock 2,这就是我要找的。谢谢!
    • 实际上,就像我在回答中解释的那样,没有必要为此升级到 Spock 2,甚至不需要为每个公司使用不同 CSV 文件的更通用的解决方案。我也解释了原因。
    • 是的,应该可以完全使用 Spock 1.3。 Spock 2.x 在 where 块和嵌套的多值数据管道中添加了多重赋值。但是显示的解决方案都没有使用这些。
    【解决方案2】:

    我不知道functionalUtilscsvFile 是如何定义的。我假设前者是@Shared 变量(否则不能在where: 块中使用),后者是在where: 块内定义的数据变量。

    我正在使用multi-variable data pipes,这是一个已经存在几年的功能,可以选择与multi-variable assignment(Spock 2.0-M3 中引入的一个功能)结合使用。这个向后兼容的测试版本可以在 Spock 1.3 和 2.0 中运行:

    package de.scrum_master.q66485337
    
    import spock.lang.Shared
    import spock.lang.Specification
    import spock.lang.Unroll
    
    class MultipleCompaniesCSVTest extends Specification {
      @Shared
      def functionalUtils = new FunctionalUtils()
    
      @Unroll
      def "company #company, row #row"() {
        expect:
        csvFile.startsWith(company)
        firstName.contains("-$company-")
        lastName.contains("-$company-")
        firstName.endsWith("-$id")
        lastName.endsWith("-$id")
    
        where:
        [company, csvFile, row] << getRecordsForCompanies("A", "B", "C", "D")
        id = row[0]; firstName = row[1]; lastName = row[2]
        // Since Spock 2.0-M3 you can simplify to:
        // (id, firstName, lastName) = row
      }
    
      private List<List> getRecordsForCompanies(String... companies) {
        def records = []
        companies.each { company ->
          def csvFile = "${company}.csv"
          functionalUtils.getRecordsFromCsv(csvFile).each { row ->
            records.add([company, csvFile, row])
          }
        }
        records
      }
    
      static class FunctionalUtils {
        def getRecordsFromCsv(String csvFile) {
          def company = csvFile.replaceFirst("[.]csv\$", "")
          // Emulate company-specific CSV file, creating some records
          (1..3).collect { id -> [id, "John-$company-$id", "Doe-$company-$id"] }
        }
      }
    }
    

    从 2.0-M3 开始,您可以根据需要将 id = row[0]; firstName = row[1]; lastName = row[2] 简化为 (id, firstName, lastName) = row

    顺便说一句,在 Spock 2 中,展开是默认设置,因此您也可以在此处省略 @Unroll

    免责声明:可能有一种更实用的常规方法来创建数据管道的值,而无需在闭包或辅助方法中强制修改本地数据结构的状态,然后返回最终结果。也许还有一些额外的迭代方法,类似于permutations()combinations()。如果是这样,我还没有找到它,不是 Groovy 专家。

    【讨论】:

    • 我认为您得到的结果类似于 Leonard 的回答。谢谢!
    • 不,伦纳德的回答是针对一个更简单的案例,就像他解释的那样。如果您查看我的代码,您会发现我假设每个 CSV 文件都是特定于公司的,具有不同的内容。这使得必要的逻辑更加复杂,无法像伦纳德建议的那样处理。如果我假设您想为多家公司读取完全相同的 CSV 文件,我当然会建议与他相同。我什至在免责声明中提到了combinations()。实际上,我真的很惊讶你的情况如此简单。真的吗?
    • 顺便说一句,我稍微更新了示例代码,以便通过将词法闭包分解到新的辅助方法 getRecordsForCompanies(String...) 来使 where: 块看起来更好。现在应该更容易理解发生了什么。
    • 在这种情况下,就像使用 Leonards 答案一样简单。但是,我将来可以看到您的答案的用例,所以我非常感激。
    猜你喜欢
    • 1970-01-01
    • 2016-06-14
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 2016-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多