在cucumber-jvm步骤之间传递变量的好习惯

tro*_*oig 40 java cucumber cucumber-jvm

要在步骤之间传递变量,我现在做的事情如下:

Feature: Demo

  Scenario: Create user
    Given User creation form management
    When Create user with name "TEST"
    Then User is created successfully
Run Code Online (Sandbox Code Playgroud)

带有步骤定义的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
   }
Run Code Online (Sandbox Code Playgroud)

我的问题是,如果在步骤之间共享信息是好的做法吗?或者更好地将功能定义为:

Then User with name "TEST" is created successfully
Run Code Online (Sandbox Code Playgroud)

我是黄瓜jvm的新手,如果这是一个没脑子的问题,那就很抱歉.

任何帮助,将不胜感激.谢谢

Ped*_*pez 33

为了分享您需要使用世界的步骤之间的共性.在Java中,它并不像Ruby那样清晰.

引用黄瓜的创造者.

"世界"的目的有两个:

1)在场景之间隔离状态.

2)在场景中的步骤定义和挂钩之间共享数据.

如何实现这是特定于语言的.例如,在ruby中,self步骤定义中的隐式变量指向当前场景的World对象.默认情况下,这是Object的一个实例,但如果您使用World钩子,它可以是您想要的任何内容.

在Java中,您有许多(可能已连接)World对象.

与Cucumber-Java中的World相同的是具有hook或stepdef注释的所有对象.换句话说,任何带有@ Before,@ After,@ Given等注释方法的类都将针对每个场景实例化一次.

这实现了第一个目标.要实现第二个目标,您有两种方法:

a)对所有步骤定义和挂钩使用单个类

b)使用由责任[1]划分的几个类,并使用依赖注入[2]将它们相互连接.

选项a)快速分解,因为您的步骤定义代码变得混乱.这就是人们倾向于使用b)的原因.

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

[2] PicoContainer,Spring,Guice,Weld,OpenEJB,Needle

可用的依赖注入模块是:

  • 黄瓜PicoContainer的
  • 黄瓜吉斯
  • 黄瓜OpenEJB的
  • 黄瓜弹簧
  • 黄瓜焊
  • 黄瓜针

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

希望这可以帮助.


Seb*_*ose 8

可以使用实例变量在类中定义的步骤之间共享数据.如果您需要在不同类的步骤之间共享数据,您应该查看DI集成(PicoContainer是最简单的).

在您显示的示例中,我会询问是否有必要在场景中显示"TEST".用户称为TEST的事实是偶然的细节,使得场景的可读性降低.为什么不在Create_user_with_name()中生成随机名称(或硬代码)?


Bar*_*W19 5

我想说的是,有理由在步骤之间共享信息,但我认为在这种情况下情况并非如此。如果您通过测试步骤传播用户名,那么从该功能中并不清楚发生了什么。我认为最好在场景中具体说明预期的内容。我可能会做这样的事情:

Feature: Demo

  Scenario: Create user
    Given User creation form management
    When Create user with name "TEST"
    Then A user named "TEST" has been created
Run Code Online (Sandbox Code Playgroud)

然后,您的实际测试步骤可能类似于:

@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));
}
Run Code Online (Sandbox Code Playgroud)


小智 5

在纯 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();
    }
Run Code Online (Sandbox Code Playgroud)


use*_*322 5

这是我的方式:我用 spring 定义了一个自定义场景范围\每个新场景都会有一个新的上下文

\n\n
Feature      @Dummy\n  Scenario: zweites Scenario\n   When Eins\n   Then Zwei\n
Run Code Online (Sandbox Code Playgroud)\n\n

1:使用弹簧

\n\n
<properties>\n<cucumber.version>1.2.5</cucumber.version>\n<junit.version>4.12</junit.version>\n</properties>\n\n<!-- cucumber section -->\n\n\n<dependency>\n  <groupId>info.cukes</groupId>\n  <artifactId>cucumber-java</artifactId>\n  <version>${cucumber.version}</version>\n  <scope>test</scope>\n</dependency>\n<dependency>\n  <groupId>info.cukes</groupId>\n  <artifactId>cucumber-junit</artifactId>\n  <version>${cucumber.version}</version>\n  <scope>test</scope>\n</dependency>\n<dependency>\n  <groupId>junit</groupId>\n  <artifactId>junit</artifactId>\n  <version>${junit.version}</version>\n  <scope>test</scope>\n</dependency>\n\n <dependency> \n   <groupId>info.cukes</groupId> \n   <artifactId>cucumber-spring</artifactId> \n   <version>${cucumber.version}</version> \n   <scope>test</scope> \n </dependency> \n\n\n<!-- end cucumber section -->\n\n<!-- spring-stuff -->\n<dependency> \n       <groupId>org.springframework</groupId> \n       <artifactId>spring-test</artifactId> \n              <version>4.3.4.RELEASE</version> \n       <scope>test</scope> \n </dependency> \n\n   <dependency> \n       <groupId>org.springframework</groupId> \n       <artifactId>spring-context</artifactId> \n              <version>4.3.4.RELEASE</version> \n       <scope>test</scope>\n   </dependency> \n   <dependency> \n       <groupId>org.springframework</groupId> \n       <artifactId>spring-tx</artifactId> \n       <version>4.3.4.RELEASE</version> \n       <scope>test</scope>\n   </dependency> \n   <dependency> \n       <groupId>org.springframework</groupId> \n       <artifactId>spring-core</artifactId> \n       <version>4.3.4.RELEASE</version> \n       <scope>test</scope>\n       <exclusions> \n           <exclusion> \n               <groupId>commons-logging</groupId> \n               <artifactId>commons-logging</artifactId> \n           </exclusion> \n       </exclusions> \n   </dependency> \n   <dependency> \n       <groupId>org.springframework</groupId> \n       <artifactId>spring-beans</artifactId> \n              <version>4.3.4.RELEASE</version> \n       <scope>test</scope>\n   </dependency> \n\n   <dependency> \n       <groupId>org.springframework.ws</groupId> \n       <artifactId>spring-ws-core</artifactId> \n       <version>2.4.0.RELEASE</version> \n       <scope>test</scope>\n   </dependency> \n
Run Code Online (Sandbox Code Playgroud)\n\n

2:构建自定义范围类

\n\n
import org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\n@Component\n@Scope(scopeName="scenario")\npublic class ScenarioContext {\n\n    public Scenario getScenario() {\n        return scenario;\n    }\n\n    public void setScenario(Scenario scenario) {\n        this.scenario = scenario;\n    }\n\n    public String shareMe;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

3:stepdef中的用法

\n\n
@ContextConfiguration(classes = { CucumberConfiguration.class })\npublic class StepdefsAuskunft {\n\nprivate static Logger logger = Logger.getLogger(StepdefsAuskunft.class.getName());\n\n@Autowired\nprivate ApplicationContext applicationContext;\n\n// Inject service here : The impl-class need @Primary @Service\n// @Autowired\n// IAuskunftservice auskunftservice;\n\n\npublic ScenarioContext getScenarioContext() {\n    return (ScenarioContext) applicationContext.getBean(ScenarioContext.class);\n}\n\n\n@Before\npublic void before(Scenario scenario) {\n\n    ConfigurableListableBeanFactory beanFactory = ((GenericApplicationContext) applicationContext).getBeanFactory();\n    beanFactory.registerScope("scenario", new ScenarioScope());\n\n    ScenarioContext context = applicationContext.getBean(ScenarioContext.class);\n    context.setScenario(scenario);\n\n    logger.fine("Context f\xc3\xbcr Scenario " + scenario.getName() + " erzeugt");\n\n}\n\n@After\npublic void after(Scenario scenario) {\n\n    ScenarioContext context = applicationContext.getBean(ScenarioContext.class);\n    logger.fine("Context f\xc3\xbcr Scenario " + scenario.getName() + " gel\xc3\xb6scht");\n\n}\n\n\n\n@When("^Eins$")\npublic void eins() throws Throwable {\n    System.out.println(getScenarioContext().getScenario().getName());\n    getScenarioContext().shareMe = "demo"\n    // you can save servicecall here\n}\n\n@Then("^Zwei$")\npublic void zwei() throws Throwable {\n    System.out.println(getScenarioContext().getScenario().getName());\n    System.out.println(getScenarioContext().shareMe);\n    // you can use last service call here\n}\n\n\n@Configuration\n    @ComponentScan(basePackages = "i.am.the.greatest.company.cucumber")\n    public class CucumberConfiguration {\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

范围类别

\n\n
import java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.ObjectFactory;\nimport org.springframework.beans.factory.config.Scope;\n\n\npublic class ScenarioScope implements Scope {\n\n\n  private Map<String, Object> objectMap = Collections.synchronizedMap(new HashMap<String, Object>());\n\n    /** (non-Javadoc)\n     * @see org.springframework.beans.factory.config.Scope#get(java.lang.String, org.springframework.beans.factory.ObjectFactory)\n     */\n    public Object get(String name, ObjectFactory<?> objectFactory) {\n        if (!objectMap.containsKey(name)) {\n            objectMap.put(name, objectFactory.getObject());\n        }\n        return objectMap.get(name);\n\n    }\n\n    /** (non-Javadoc)\n     * @see org.springframework.beans.factory.config.Scope#remove(java.lang.String)\n     */\n    public Object remove(String name) {\n        return objectMap.remove(name);\n    }\n\n    /** (non-Javadoc)\n     * @see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String, java.lang.Runnable)\n     */\n    public void registerDestructionCallback(String name, Runnable callback) {\n        // do nothing\n    }\n\n    /** (non-Javadoc)\n     * @see org.springframework.beans.factory.config.Scope#resolveContextualObject(java.lang.String)\n     */\n    public Object resolveContextualObject(String key) {\n        return null;\n    }\n\n    /** (non-Javadoc)\n     * @see org.springframework.beans.factory.config.Scope#getConversationId()\n     */\n    public String getConversationId() {\n        return "VolatileScope";\n    }\n\n    /**\n     * vaporize the beans\n     */\n    public void vaporize() {\n        objectMap.clear();\n    }\n\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

  • 每当 Spring 进入时,方程的复杂性就会真正跃升。(这不是海报的错,因为他遵循了 Spring 的食谱。)我的意思是我们刚刚增加了两倍?代码行,都是结构代码。不是“完成工作”代码。这还不是全部。在镜头外,您已经获得了 SpringCofiguration 和场景。这是一个很好的例子(尽管缺少这两部分)。但谁愿意经历所有这些苦差事来让 Cucumber 获得共享状态呢? (2认同)