#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. Ты меня опередил. Потрясающе!