Статьи

Как определить сценарии с помощью JBehave и Guice


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