[英]How to pass variables between cucumber-jvm steps
為了在步驟之間傳遞變量,我讓步驟方法屬於同一個類,並使用類的字段作為傳遞的信息。
下面是一個例子:
Feature: Demo
Scenario: Create user
Given User creation form management
When Create user with name "TEST"
Then User is created successfully
帶有步驟定義的 Java 類:
public class CreateUserSteps {
private String userName;
@Given("^User creation form management$")
public void User_creation_form_management() throws Throwable {
// ...
}
@When("^Create user with name \"([^\"]*)\"$")
public void Create_user_with_name(String userName) throws Throwable {
//...
this.userName = userName;
}
@Then("^User is created successfully$")
public void User_is_created_successfully() throws Throwable {
// Assert if exists an user with name equals to this.userName
}
我的問題是在步驟之間共享信息是否是一個好習慣? 或者最好將該功能定義為:
Then User with name "TEST" is created successfully
為了在步驟之間共享共性,您需要使用World 。 在 Java 中,它不像在 Ruby 中那樣清晰。
引用 Cucumber 的創造者。
“世界”的目的有兩個:
隔離場景之間的狀態。
在場景中的步驟定義和掛鈎之間共享數據。
這是如何實現的是特定於語言的。 例如,在 ruby 中,步驟定義中的隱式
self
變量指向當前場景的 World 對象。 默認情況下,這是 Object 的一個實例,但如果您使用 World 鈎子,它可以是您想要的任何東西。在 Java 中,您有許多(可能連接的) World 對象。
Cucumber-Java 中 World 的等價物是所有帶有 hook 或 stepdef 注釋的對象。 換句話說,任何帶有@Before、@After、@Given 等注釋的方法的類都將在每個場景中被實例化一次。
這樣就達到了第一個目標。 要實現第二個目標,您有兩種方法:
a) 為所有步驟定義和掛鈎使用單個類
b) 使用按職責划分的幾個類[1],並使用依賴注入[2]將它們相互連接起來。
選項 a) 很快就會崩潰,因為您的步驟定義代碼變得一團糟。 這就是為什么人們傾向於使用 b)。
[1] https://cucumber.io/docs/gherkin/step-organization/
[2] PicoContainer、Spring、Guice、Weld、OpenEJB、Needle
可用的依賴注入模塊有:
原始帖子在這里https://groups.google.com/forum/#!topic/cukes/8ugcVreXP0Y 。
希望這可以幫助。
可以使用實例變量在類中定義的步驟之間共享數據。 如果您需要在不同類中的步驟之間共享數據,您應該查看 DI 集成(PicoContainer 是最簡單的)。
在您展示的示例中,我會問是否有必要在場景中顯示“TEST”。 用戶被稱為 TEST 的事實是一個附帶的細節,這會降低場景的可讀性。 為什么不在 Create_user_with_name() 中生成隨機名稱(或硬編碼)?
在純 java 中,我只使用一個單例對象,該對象創建一次並在測試后清除。
public class TestData_Singleton {
private static TestData_Singleton myself = new TestData_Singleton();
private TestData_Singleton(){ }
public static TestData_Singleton getInstance(){
if(myself == null){
myself = new TestData_Singleton();
}
return myself;
}
public void ClearTestData(){
myself = new TestData_Singleton();
}
我會說有理由在步驟之間共享信息,但我認為在這種情況下情況並非如此。 如果您通過測試步驟傳播用戶名,那么該功能並不清楚發生了什么。 我認為最好在場景中具體說明預期的內容。 我可能會做這樣的事情:
Feature: Demo
Scenario: Create user
Given User creation form management
When Create user with name "TEST"
Then A user named "TEST" has been created
然后,您的實際測試步驟可能類似於:
@When("^Create user with name \"([^\"]*)\"$")
public void Create_user_with_name(String userName) throws Throwable {
userService.createUser(userName);
}
@Then("^A user named \"([^\"]*)\" has been created$")
public void User_is_created_successfully(String userName) throws Throwable {
assertNotNull(userService.getUser(userName));
}
這是我的方式:我用 spring 定義了一個自定義的場景范圍,每個新場景都會有一個新的上下文
Feature @Dummy
Scenario: zweites Scenario
When Eins
Then Zwei
1:使用彈簧
<properties>
<cucumber.version>1.2.5</cucumber.version>
<junit.version>4.12</junit.version>
</properties>
<!-- cucumber section -->
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-spring</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<!-- end cucumber section -->
<!-- spring-stuff -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.4.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.4.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.4.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.4.RELEASE</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.4.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>2.4.0.RELEASE</version>
<scope>test</scope>
</dependency>
2:構建自定義范圍類
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(scopeName="scenario")
public class ScenarioContext {
public Scenario getScenario() {
return scenario;
}
public void setScenario(Scenario scenario) {
this.scenario = scenario;
}
public String shareMe;
}
3:在stepdef中的使用
@ContextConfiguration(classes = { CucumberConfiguration.class })
public class StepdefsAuskunft {
private static Logger logger = Logger.getLogger(StepdefsAuskunft.class.getName());
@Autowired
private ApplicationContext applicationContext;
// Inject service here : The impl-class need @Primary @Service
// @Autowired
// IAuskunftservice auskunftservice;
public ScenarioContext getScenarioContext() {
return (ScenarioContext) applicationContext.getBean(ScenarioContext.class);
}
@Before
public void before(Scenario scenario) {
ConfigurableListableBeanFactory beanFactory = ((GenericApplicationContext) applicationContext).getBeanFactory();
beanFactory.registerScope("scenario", new ScenarioScope());
ScenarioContext context = applicationContext.getBean(ScenarioContext.class);
context.setScenario(scenario);
logger.fine("Context für Scenario " + scenario.getName() + " erzeugt");
}
@After
public void after(Scenario scenario) {
ScenarioContext context = applicationContext.getBean(ScenarioContext.class);
logger.fine("Context für Scenario " + scenario.getName() + " gelöscht");
}
@When("^Eins$")
public void eins() throws Throwable {
System.out.println(getScenarioContext().getScenario().getName());
getScenarioContext().shareMe = "demo"
// you can save servicecall here
}
@Then("^Zwei$")
public void zwei() throws Throwable {
System.out.println(getScenarioContext().getScenario().getName());
System.out.println(getScenarioContext().shareMe);
// you can use last service call here
}
@Configuration
@ComponentScan(basePackages = "i.am.the.greatest.company.cucumber")
public class CucumberConfiguration {
}
范圍類
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
public class ScenarioScope implements Scope {
private Map<String, Object> objectMap = Collections.synchronizedMap(new HashMap<String, Object>());
/** (non-Javadoc)
* @see org.springframework.beans.factory.config.Scope#get(java.lang.String, org.springframework.beans.factory.ObjectFactory)
*/
public Object get(String name, ObjectFactory<?> objectFactory) {
if (!objectMap.containsKey(name)) {
objectMap.put(name, objectFactory.getObject());
}
return objectMap.get(name);
}
/** (non-Javadoc)
* @see org.springframework.beans.factory.config.Scope#remove(java.lang.String)
*/
public Object remove(String name) {
return objectMap.remove(name);
}
/** (non-Javadoc)
* @see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String, java.lang.Runnable)
*/
public void registerDestructionCallback(String name, Runnable callback) {
// do nothing
}
/** (non-Javadoc)
* @see org.springframework.beans.factory.config.Scope#resolveContextualObject(java.lang.String)
*/
public Object resolveContextualObject(String key) {
return null;
}
/** (non-Javadoc)
* @see org.springframework.beans.factory.config.Scope#getConversationId()
*/
public String getConversationId() {
return "VolatileScope";
}
/**
* vaporize the beans
*/
public void vaporize() {
objectMap.clear();
}
}
如果您使用帶有黃瓜的 Serenity 框架,則可以使用當前會話。
Serenity.getCurrentSession()
有關此功能的更多信息,請訪問http://thucydides-webtests.com/2012/02/22/managing-state-between-steps/ 。 (寧靜以前叫修昔底德)
另一種選擇是使用 ThreadLocal 存儲。 創建上下文映射並將它們添加到映射中。 Cucumber JVM 在同一個線程中運行所有步驟,您可以在所有步驟中訪問它。 為了更容易,您可以在鈎子之前實例化存儲並在鈎子之后清除。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.