Как выполнить метод установки / блокировать только один раз для каждого функционального метода?

#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 , будут равны нулю после первой итерации.