Set-up/clean-up 每个带有 'where:' 块的特征方法只有一次

Set-up/clean-up only once per feature method with 'where:' block

Spock 的 setupSpec 规格 class 级别。对于单个测试用例级别,我希望有类似的东西。

这在 Spock 中可能不可用,有人对此有解决方法吗?

void "test something"() {
    setup:
    User user = createUser()

    when:
    user.setAdress(new Address(zipCode: inputZipCode, city: inputCity))

    then:
    user.address.city == inputCity
    user.address.zipCode == inputZipCode

    cleanup:
    deleteUser(user)

    where:
    inputCity | inputZipCode
    "a" |"1"
    "b" |"2"
}

每次迭代后都不需要创建和删除用户。

我觉得这很丑陋,因为它涉及到手工记账。我不建议你这样做,但无论如何:

package de.scrum_master.Whosebug.q57721328

import spock.lang.See
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll

class OneTimeSetupCleanupParametrisedTest extends Specification {
  @Shared User user
  @Shared int iteration

  User createUser() {
    // Set up test fixture only if iteration == 0 (or optionally, if fixture is uninitialised) 
    user ?: new User()
  }

  void deleteUser(User userInstance) {
    // Clean up counter (and test fixture, if any) only if iteration == total number of iterations
    if (++iteration == specificationContext.currentIteration.estimatedNumIterations) {
      userInstance.delete()
      user = null
      iteration = 0
    }
  }

//  @Unroll
  void "test something"() {
    setup:
    // Call initialiser helper for each iteration, relying on the fact that it will only
    // create a text fixture if none exists yet
    user = createUser()

    when:
    user.setAdress(new Address(zipCode: inputZipCode, city: inputCity))

    then:
    user.address.city == inputCity
    user.address.zipCode == inputZipCode

    cleanup:
    // Call clean-up helper for each iteration, relying on the fact that it will only
    // clean up the fixture during the last iteration
    deleteUser(user)

    where:
    inputCity | inputZipCode
    "a"       | "1"
    "b"       | "2"
  }

  static class User {
    Address address

    User() {
      println "creating user"
    }

    void setAdress(Address address) {
      this.address = address
    }

    void delete() {
      println "deleting user"
      address = null
    }
  }

  static class Address {
    String zipCode, city
  }
}

控制台日志:

creating user
deleting user

更新: Spock 手册说 about this topic

Sharing of Objects between Iterations

In order to share an object between iterations, it has to be kept in a @Shared or static field.

NOTE: Only @Shared and static variables can be accessed from within a where: block.

Note that such objects will also be shared with other methods. There is currently no good way to share an object just between iterations of the same method. If you consider this a problem, consider putting each method into a separate spec, all of which can be kept in the same file. This achieves better isolation at the cost of some boilerplate code.