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"
}
每次迭代后都不需要创建和删除用户。
是否可以为单个测试设置 la- setupSpec
而不是 class 级别?
可以操纵测试用例以使用 class-setupSpec/CleanupSpec 甚至创建一个新测试(使用@Stepwise)来实现这一点,但我正在寻找好的解决方案而不是 hack .
我觉得这很丑陋,因为它涉及到手工记账。我不建议你这样做,但无论如何:
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.
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"
}
每次迭代后都不需要创建和删除用户。
是否可以为单个测试设置 la-
setupSpec
而不是 class 级别?可以操纵测试用例以使用 class-setupSpec/CleanupSpec 甚至创建一个新测试(使用@Stepwise)来实现这一点,但我正在寻找好的解决方案而不是 hack .
我觉得这很丑陋,因为它涉及到手工记账。我不建议你这样做,但无论如何:
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
andstatic
variables can be accessed from within awhere:
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.