Если вы используете 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+.