Если вы используете JBehave с инфраструктурой внедрения зависимостей, такой как Spring или Guice, вы можете быстро понять, что определение области действия классов Step становится немного сложнее. По умолчанию обе эти инфраструктуры предоставляют две основные области: область действия экземпляра (или прототипа) и область действия синглтона.
Ни один из этих вариантов не подходит для использования с JBehave. Область действия экземпляра является проблемой, потому что JBehave затем создает новый класс Step для каждого шага в сценарии, делая невозможным совместное использование состояния между шагами без использования статического (глобального) состояния. Одиночная область — это другая сторона той же проблемы: состояние в конечном итоге становится общим для
всех сценариев. В любом случае, чтобы все заработало, вы должны помнить о необходимости очищать глобальное состояние после каждого сценария.
Более простое решение будет заключаться в реализации настраиваемой области действия сценария. Я покажу вам, как сделать это для Guice ниже.
Во-первых, нам нужно определить новую пользовательскую область, реализовав интерфейс Scope Guice
. Этот класс будет контейнером, который добавляет и управляет нашими зависимостями при вводе области и удаляет их при выходе из области. Это может быть пугающим и подверженным ошибкам, но, к счастью, разработчики Guice предоставили нам по умолчанию
SimpleScope , который делает все это за нас. Этот класс достаточен для наших нужд, и вы можете скопировать его прямо в исходный код.
Во-вторых, нам нужно сообщить JBehave, когда на самом деле создавать новую область, а когда закрывать область. Поскольку мы хотим охватить наши зависимости сценариями, мы используем аннотации @BeforeScenario и @AfterScenario компании JBehave для входа и выхода из каждой области. Обратите внимание, что мы должны внедрить нашу копию SimpleScope, которая фактически управляет зависимостями области.
public class ScenarioContext {
private SimpleScope scope;
@Inject
public ScenarioContext( @Named ( "scenarioScope" ) SimpleScope scope ) {
this.scope = scope;
}
@BeforeScenario
public void beforeScenario() {
scope.enter();
}
@AfterScenario
public void afterScenario() {
scope.exit();
}
}
В-третьих, так же, как
аннотация Singleton , нам нужна аннотация привязки, чтобы сообщить Guice о том, как бы мы хотели, чтобы наши классы шагов были ограничены. Мы будем использовать эту новую аннотацию для привязки экземпляров к нашему новому классу SimpleScope. Мы создаем это просто так:
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import com.google.inject.ScopeAnnotation;
@Target ( { TYPE, METHOD } )
@Retention ( RUNTIME )
@ScopeAnnotation
public @interface ScenarioScope {}
Заключительный этап состоит из двух частей. Первый заключается в том, чтобы на самом деле связать наши классы шагов с нашей новой областью действия, что достигается простым предоставлением файла класса в связыватель с помощью .in (). Однако нам также необходимо сообщить Guice о том, как управлять контейнером SimpleScope.
public class AppModule extends AbstractModule {
@Override
protected void configure() {
setUpScenarioScope();
bind( MySteps.class ).in( ScenarioScope.class );
}
private void setUpScenarioScope() {
bindScope( ScenarioScope.class, scenarioScope );
SimpleScope scenarioScope = new SimpleScope();
bind( SimpleScope.class ).annotatedWith( Names.named( "scenarioScope" ) ).toInstance( scenarioScope );
bind( ScenarioContext.class ).in( Singleton.class );
}
}
Вышеуказанный метод setUpScenarioScope () выполняет несколько действий:
- сообщает Guice о нашей новой области, используя bindScope ()
- создает экземпляр нашего класса SimpleScope для управления зависимостями (нам нужна только одна)
- гарантирует, что экземпляр может быть внедрен в наш JBehave-аннотированный контекстный класс
- связывает этот контекст в области синглтона
Это оно! Все шаги, аннотированные для области действия сценария, смогут совместно использовать данные в одном классе шагов, гарантируя при этом новый набор шагов для каждого нового сценария.
Известная проблема : этот подход в настоящее время не совместим с
библиотекой jbehave-junit-runner . Эта библиотека создает специальный бегун JBehave, который форматирует результаты теста в стандартный вывод JUnit, и опирается на более старую копию JBehave, которая вызывает проблему «курица и яйцо» при создании шага.
Патч был представлен , чтобы исправить это, но на сегодняшний день она не была включена в релиз. Обходной путь — собрать из исходного кода и применить патч вручную, а также убедиться, что вы используете JBehave 3.8+.