#java #groovy #spock
#ява #groovy #спок #java
Вопрос:
У меня есть простой тестовый класс groovy amp; spock, написанный в spring boot, с setup
, expect
cleanup
и where
block , но проблема здесь в том, что setup
блок выполняется для каждой итерации в блоке where, что не так, как мне нужно. итак, мой вопрос: есть ли способ заставить setup()
метод выполняться только один раз перед каждым методом
import spock.lang.Specification
class SpockSpec extends Specification {
def setupSpec() { println 'setupSpec()' }
def setup() { println 'setup()' }
def cleanup() { println 'cleanup()' }
def cleanupSpec() { println 'cleanupSpec()' }
def 'test blocks'() {
setup:
println 'setup:'
expect:
println "expect: $data"
cleanup:
println 'cleanup:'
where:
data << [1, 2]
}
}
Вывод :
setupSpec()
setup()
setup:
expect: 1
cleanup:
cleanup()
setup()
setup:
expect: 2
cleanup:
cleanup()
cleanupSpec()
Ожидаемый результат:. // setup()
метод выполняется только один раз перед методом, а не для каждой итерации в where
setupSpec()
setup()
setup:
expect: 1
cleanup:
cleanup()
setup:
expect: 2
cleanup:
cleanup()
cleanupSpec()
Комментарии:
1. Почему вы не используете
setupSpec()
?2. Я думаю, что ваша тема «Как выполнить установочный блок после всех итераций where block в тестовом классе groovy spock» не отражает того, что вы действительно хотите сделать. Я собираюсь изменить его на то, что я считаю правильным, в соответствии с тем, что вы описываете в своем вопросе. Пожалуйста, перепроверьте. Спасибо.
3. Дружеское напоминание: пожалуйста, предоставьте обратную связь людям, пытающимся вам помочь. После того, как вы это сделаете, я удалю этот комментарий. 😀
4. Привет @kriegaex я полностью забыл об этом, но в соответствии с вашим ответом я могу использовать
setupSpec
, поскольку я хочу, чтобы после каждого метода тестирования необходимо выполнить некоторый код очистки базы данных5.
setupSpec()
выполняется перед первой функцией спецификации, а не после чего-либо. Если вы хотите, чтобы код выполнялся «после каждого метода тестирования», как вы сказали, вам нужно использоватьcleanup()
метод. Но это противоречит вашим заявлениям в вопросе, что довольно сбивает с толку. Итак, что же теперь будет? Пожалуйста, примите решение и общайтесь согласованным образом.
Ответ №1:
Короткий ответ: это не то, как работает Spock. Оба setup()
и setup:
предназначены для выполнения для каждой итерации параметризованного теста, потому что логически каждая итерация является независимым методом функции. Я думаю, что это хорошо, как это.
Леонард предложил:
Почему вы не используете
setupSpec()
?
setupSpec()
не будет сокращать его здесь, если существует более одного функционального метода.
Вы могли бы написать расширение Spock, глобальное или управляемое аннотациями, которое могло бы помочь вам пропустить setup()
выполнение при определенных обстоятельствах. Я обсудил оба варианта с Марцином Эрдманном, сопровождающим Geb, в списке рассылки Geb в этой теме. Вы могли бы скопировать код для варианта, который вы предпочитаете, и настроить его в соответствии с вашими потребностями, т. Е. Вместо того, чтобы всегда пропускать, setup()
когда метод объекта аннотируется пользовательской @SkipSetup
аннотацией, вы могли бы расширить аннотацию, чтобы использовать закрытие для оценки в качестве условия или просто жестко запрограммировать пропуск всех итераций, кроме первой для гипотетической @SetupOnce
аннотации.
Или вы могли бы просто реорганизовать свои тесты, чтобы использовать canonical Spock, например, заставить ваш установочный код делать что-то, только если @Shared
переменная имеет определенное значение. Я не видел ваш код, но мне кажется, что если вы считаете, что вам нужна эта функция, что-то не так с вашим дизайном тестирования.
Обновление: я думал о чем-то подобном:
Расширение Spock, управляемое аннотациями:
package de.scrum_master.testing.extension
import org.spockframework.runtime.extension.ExtensionAnnotation
import java.lang.annotation.Retention
import java.lang.annotation.Target
import static java.lang.annotation.ElementType.METHOD
import static java.lang.annotation.RetentionPolicy.RUNTIME
@Retention(RUNTIME)
@Target(METHOD)
@ExtensionAnnotation(SetupOnceExtension)
@interface SetupOnce {}
package de.scrum_master.testing.extension
import org.spockframework.runtime.extension.AbstractMethodInterceptor
import org.spockframework.runtime.extension.IMethodInvocation
class SetupOnceMethodInterceptor extends AbstractMethodInterceptor {
Map<String, Boolean> annotatedFeatures = new HashMap<>()
@Override
void interceptSetupMethod(IMethodInvocation invocation) throws Throwable {
if (annotatedFeatures.containsKey(invocation.feature.name)) {
if (!annotatedFeatures[invocation.feature.name]) {
invocation.proceed()
annotatedFeatures[invocation.feature.name] = true
}
}
else
invocation.proceed()
}
}
package de.scrum_master.testing.extension
import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension
import org.spockframework.runtime.model.FeatureInfo
class SetupOnceExtension extends AbstractAnnotationDrivenExtension<SetupOnce> {
SetupOnceMethodInterceptor interceptor
@Override
void visitFeatureAnnotation(SetupOnce annotation, FeatureInfo feature) {
if (!interceptor) {
interceptor = new SetupOnceMethodInterceptor()
feature.spec.addSetupInterceptor interceptor
}
interceptor.annotatedFeatures[feature.name] = false
}
}
Пример спецификации Spock:
package de.scrum_master.testing.extension
import spock.lang.Specification
class SetupOnceTest extends Specification {
def setup() {
println "SetupOnceTest -> setup"
}
def feature1() {
setup:
println "SetupOnceTest -> feature1"
expect:
true
}
@SetupOnce
def feature2() {
setup:
println "SetupOnceTest -> feature2"
expect:
true
}
def feature3() {
setup:
println "SetupOnceTest -> feature3, iteration $count"
expect:
true
where:
count << [1, 2, 3]
}
@SetupOnce
def feature4() {
setup:
println "SetupOnceTest -> feature4, iteration $count"
expect:
true
where:
count << [1, 2, 3]
}
}
Журнал консоли:
Как вы можете видеть, для feature4()
setup()
метода выполняется только один раз. Однако расширение не влияет на setup:
блоки, они всегда будут выполняться.
SetupOnceTest -> setup
SetupOnceTest -> feature1
SetupOnceTest -> setup
SetupOnceTest -> feature2
SetupOnceTest -> setup
SetupOnceTest -> feature3, iteration 1
SetupOnceTest -> setup
SetupOnceTest -> feature3, iteration 2
SetupOnceTest -> setup
SetupOnceTest -> feature3, iteration 3
SetupOnceTest -> setup
SetupOnceTest -> feature4, iteration 1
SetupOnceTest -> feature4, iteration 2
SetupOnceTest -> feature4, iteration 3
Комментарии:
1. Обновление: я добавил пример расширения, образец теста и журнал консоли.
2.
setupSpec() won't cut it here if there is more than one feature method.
хотя это правда, вы можете легко получить спецификацию с одним методом, управляемым данными. Это самое простое решение указанной проблемы. Конечно, ваше расширение более мощное, необходимо ли это, зависит от остальной части кода, поэтому я попросил OP уточнить. Кстати, необходимо будет использовать,@Shared
иначе поля, инициализированныеsetup
, будут равны нулю после первой итерации.