2014-10-17 100 views
25

要傳遞的步驟,現在我在做類似的例子之間的變量如下:很好的做法,黃瓜,JVM之間的變量傳遞步驟

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 

我是新拌黃瓜(JVM)很抱歉,如果這是一個愚笨的問題。

任何幫助,將不勝感激。謝謝

+1

你的戰略,BDD框架,在那裏你可以鏈接一個類定義具有特定功能的文件效果很好。黃瓜不支持這個(好吧,需要比你想的更多的努力:http://confessionsofanagilecoach.blogspot.com/2017/05/teaching-cucumbers-about-boundaries.html)。更好地使用下面提到的世界戰略,或者改爲使用JBehave。 – 2017-05-03 21:25:46

回答

28

爲了分享你需要使用一個World步驟之間的共性。在Java中,它不像Ruby那樣清晰。

引用黃瓜的創造者。

「世界」 的目的有兩個:

1)場景之間的隔離狀態。

2)在場景中的步驟定義和鉤子之間共享數據。

這是如何實現的是語言特定的。例如,在ruby中,步驟定義中的隱含self變量指向 當前場景的World對象。這是默認的 Object實例,但如果您使用World鉤子,它可以是任何您想要的。

在Java中,您有許多(可能連接的)世界對象。

黃瓜-Java世界的等價物是所有的對象 帶鉤子或stepdef註釋。換句話說,任何帶有@Before,@After,@Given等註釋的 方法的類將爲 爲每個方案實例化一次。

這實現了第一個目標。爲了實現第二個目標,你有兩個 方法:

a)使用一個類爲您的所有的步驟定義和掛鉤

b)利用幾類按責任劃分[1]和使用依賴 注入[ 2]將它們連接到一起。

選項a)快速崩潰,因爲您的步驟定義代碼 變得混亂。這就是爲什麼人們傾向於使用b)。

[1] https://github.com/cucumber/cucumber/wiki/Step-Organization

[2] PicoContainer的,春天,吉斯,虛焊,OpenEJB的,針

可用依賴注入模塊有:

  • 黃瓜PicoContainer的
  • cucumber-guice
  • cucumber-openejb
  • 黃瓜彈簧
  • 黃瓜焊接
  • 黃瓜針

原帖在這裏https://groups.google.com/forum/#!topic/cukes/8ugcVreXP0Y

希望這會有所幫助。

+0

感謝您的幫助@Pedro Lopez – troig 2014-10-24 06:12:18

+0

步驟組織鏈接已經死亡。 – 2016-01-29 16:58:41

+0

已更新。謝謝! – 2016-01-30 00:02:18

5

在一個類內使用實例變量定義的步驟之間共享數據是很好的。如果你需要在不同類別的步驟之間共享數據,你應該看看DI集成(PicoContainer是最簡單的)。

在你展示的例子中,我會問是否需要在場景中顯示「TEST」。用戶被稱爲TEST的事實是一個附帶的細節,並且使得該場景的可讀性降低。爲什麼不在Create_user_with_name()中生成一個隨機名稱(或硬編碼的東西)?

+0

非常感謝您的幫助!我會深入PicoContainer。 – troig 2014-10-18 14:35:36

+0

依賴注入如何在類之間共享狀態? – fijiaaron 2016-11-03 22:14:06

+0

@fijiaaron如果您使用任何DI工具,例如pico(在pico的情況下爲構造函數注入),那麼創建步驟定義和鉤子類的責任由DI接管。爲每個場景創建這些類的新實例。加上此步驟所需的任何其他類實例,defs是由DI針對步驟def構造函數中定義的每個場景創建的。然後將每個類的相同實例傳遞給所有需要它的步驟定義。因此,狀態是圍繞不同的步驟定義傳遞的。 – Grasshopper 2016-11-05 20:35:39

2

我想說,有步驟之間共享信息的原因,但我不認爲在這種情況下是這種情況。如果您通過測試步驟傳播用戶名,那麼從功能中就不太清楚發生了什麼。我認爲在這種情況下特別說明預期會更好。我可能會做這樣的事情:

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)); 
} 
+0

感謝您的輸入@ BarrySW19。我知道我的問題的例子不是太好..但是,真的,我想知道是否共享步驟之間的信息是一個很好的做法。我會記住你的建議 – troig 2014-10-23 12:38:17

2

在純java中,我只是使用一次創建並在測試後清除的Singleton對象。

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(); 
    } 
+0

使用Cucumber的DI集成的好處是你不必在每一步之前記住「ClearTestData」 - 黃瓜爲你處理。 – 2016-11-06 03:43:38

+0

這種方法對我來說非常合適。無需更改大量代碼。 +1純java版 – Chai 2017-03-09 11:06:23

2

這裏我的方式:我定義一個定製的方案,適用範圍與春天 每一個新場景都會有一個新的背景下

Feature  @Dummy 
    Scenario: zweites Scenario 
    When Eins 
    Then Zwei 

1:使用Spring

<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:使用以s tepdef

@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(); 
    } 


} 
+0

爲什麼你需要定製Context,是不是Prototype就夠了? – nahab 2017-01-26 13:55:42

+2

任何時候春天進入方程複雜性真的跳躍。 (當他遵循Spring的食譜時,不是海報的錯。)我的意思是我們只有三倍?代碼行和它的全部結構代碼。沒有「完成工作」的代碼。這並不是全部。關閉相機,你已經有了SpringCofiguration和Scenario。這是一個很好的例子(儘管錯過了這兩個部分)。但誰願意通過所有的苦差事來使黃瓜獲得共享狀態? – 2017-05-03 21:35:00