Аннотация Spring Cucumber ActiveProfiles не работает с CucumberContext

#spring-boot #cucumber #spring-profiles #spring-context

#spring-boot #cucumber #spring-профили #spring-контекст

Вопрос:

Я работаю над проектом, в котором у нас есть компонент, который состоит:

  • Ядро
  • соединитель с внешней системой 1
  • соединитель с внешней системой 2

Соединители являются взаимоисключающими (если connector1 активен, connector2 всегда неактивен и наоборот). Ядро и один соединитель автоматически подключаются при запуске ApplicationContext. Какой экземпляр соединителя создается, зависит от значения в свойствах приложения spring.

Мы пишем интеграционные тесты с использованием spring-cucumber (версия 6.2.2). Для каждой внешней системы я хочу запустить набор тестов cucumber. Я создал 2 набора тестов, используя аннотации к сценарию cucumber, что позволяет мне разделить тесты для connector1 и connector2.

Проблема, с которой я сталкиваюсь, заключается в том, что мне нужны оба набора тестов для запуска с другим профилем spring, поэтому я могу использовать другую конфигурацию. Я не могу найти, как это сделать.

Текущая реализация (с одним профилем):

pom.xml

 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.4.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-java</artifactId>
    <version>6.2.2</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-junit</artifactId>
    <version>6.2.2</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-spring</artifactId>
    <version>6.2.2</version>
    <scope>test</scope>
</dependency>
  

CucumberConnector1IT.java

 package omitted.for.company.rules;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(
        features = { "classpath:feature/" },
        glue = { "omitted.for.company.rules.cucumber.step" },
        plugin = { "pretty", "json:target/cucumber-report/cucumber.json",
                "html:target/cucumber-report/cucumber.html" },
        tags = "@Connector1 and not @ignore" // tags make sure only applicable tests are run
)
public class CucumberConnector1IT {
}
  

CucumberConnector2IT.java

 package omitted.for.company.rules;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(
        features = { "classpath:feature/" },
        glue = { "omitted.for.company.rules.cucumber.step" },
        plugin = { "pretty", "json:target/cucumber-report/cucumber.json",
                "html:target/cucumber-report/cucumber.html" },
        tags = "@Connector2 and not @ignore" // tags make sure only applicable tests are run
)
public class CucumberConnector2IT {
}
  

StepInitializer.java

 package omitted.for.company.rules.steps;

import io.cucumber.java.Before;
import io.cucumber.spring.CucumberContextConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;

@SpringBootTest
@ActiveProfiles("test") // I can only get this to work if the @ActiveProfiles and @CucumberContextConfiguration annotations are on the same class
@CucumberContextConfiguration
public class StepInitializer {

    // mock some beans to use in cucumber test
    @MockBean
    private EventProducer eventProducer; 
    @MockBean
    private RestInterface restInterface;

    @Before
    public void setup() {
    }
}
  

Пока все работает. Но что мне нужно сейчас, так это поместить @ActiveProfiles() аннотацию в другой класс, отличный от @CucumberContextConfiguration . Если я смогу это сделать, я смогу аннотировать правильные классы шагов с требуемыми профилями.

Проблема в том, что я недостаточно хорошо понимаю аннотации spring, чтобы знать, какие из них я могу перемещать, а какие нет. Я нашел этот пример именно того, что я пытаюсь сделать (репозиторий spring-cucumber-profiles, обратите внимание на расположение @ActiveProfiles аннотации здесь). К сожалению, он использует более старую версию cucumber-spring (v5.6.0). В этой версии еще нет @CucumberContextConfiguration аннотации, и она выполняет некоторую магию с контекстом spring в соответствии с документацией (примечания к выпуску cucumber-spring). Я попытался проверить пример репозитория и обновить его до версии 6.2.2, но не смог заставить его работать с новой версией.

Если кто-нибудь заметит, что я делаю неправильно в моих собственных примерах, или у него есть возможность получить пример репозитория, работающего с версией 6.2.2 cucumber-spring , я был бы очень признателен.

Заранее спасибо! 🙂

Ответ №1:

Я решил проблему, немного разделив пакеты и создав отдельные StepInitializer классы для обоих наборов тестов.

Текущая настройка:

Тестовый бегун:

 package omitted.for.company.rules;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(
        features = { "classpath:feature/" },
        extraGlue = { "omitted.for.company.rules.cucumber.step.common" }, // used extraGlue instead of glue
        plugin = { "pretty", "json:target/cucumber-report/cucumber.json",
                "html:target/cucumber-report/cucumber.html" },
        tags = "@Connector1 and not @ignore" // tags make sure only applicable tests are run
)
public class CucumberConnector1IT {
}
  

Конфигурация контекста:

 package omitted.for.company.rules.steps;

import io.cucumber.java.Before;
import io.cucumber.spring.CucumberContextConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;

@SpringBootTest
@ActiveProfiles({ "test", "test-connector1" })
@CucumberContextConfiguration
public class Connector1StepInitializer {

    // mock some beans to use in cucumber test
    @MockBean
    private EventProducer eventProducer; 
    @MockBean
    private RestInterface restInterface;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private Environment environment;

    @Before
    public void setup() {
        assertThat(applicationContext).isNotNull();
        assertThat(environment.getActiveProfiles()).containsOnly("test","test-connector1");
    }
}
  

Оба коннектора / тестовые исполнители имеют свой собственный класс runner и свой собственный класс ContextConfiguration .

Очень важно, чтобы классы, содержащие @CucumberContextConfiguration аннотацию, не находились в общем пакете glue (как указано в extraGlue свойстве в @CucumberOptions аннотации).

Структура пакета выглядит следующим образом:

 ├───common
│   └───step                                // Contains shared steps. This path should be in the 'extraGlue' field of the runner classes
├───connector1
│   │   CucumberConnector1IT.java           // Runner 1
│   └───step
│           Connector1Steps.java            // Specific steps
│           Connector1StepInitializer.java  // has @ActiveProfiles and @CucumberContextConfiguration annotations, use to mock beans
└───connector2
    │   CucumberConnector1IT.java           // Runner 2
    └───step
            Connector2Steps.java            // Specific steps
            Connector2StepInitializer.java  // has @ActiveProfiles and @CucumberContextConfiguration annotations, use to mock beans
  

Таким образом, я все еще могу использовать разные профили spring :).

Комментарии:

1. Ты меня опередил. Потрясающе!