要在步骤之间传递变量,我现在正在执行如下示例:
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的新手,很抱歉,如果这是一个没脑子的问题。
任何帮助将不胜感激。感谢
答案 0 :(得分:30)
为了分享步骤之间的共性,您需要使用World。在Java中,它并不像Ruby那样清晰。
引用黄瓜的创造者。
“世界”的目的有两个:
1)在场景之间隔离状态。
2)在场景中的步骤定义和挂钩之间共享数据。
如何实现这是特定于语言的。例如,在红宝石中, 步骤定义中的隐式
self
变量指向 当前场景的世界对象。默认情况下,这是一个实例 对象,但如果你使用World hook,它可以是你想要的任何东西。在Java中,您有许多(可能已连接)World对象。
World in Cucumber-Java相当于所有对象 with hook或stepdef annotations 。换句话说,任何类都有 用@ 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
可用的依赖注入模块是:
此处的原始帖子https://groups.google.com/forum/#!topic/cukes/8ugcVreXP0Y。
希望这有帮助。
答案 1 :(得分:8)
使用实例变量在类中定义的步骤之间共享数据是很好的。如果您需要在不同类的步骤之间共享数据,您应该查看DI集成(PicoContainer是最简单的)。
在您展示的示例中,我会询问是否显示" TEST"在场景中是必要的。用户称为TEST的事实是偶然的细节,使得场景的可读性降低。为什么不在Create_user_with_name()?
中生成随机名称(或硬代码)答案 2 :(得分:3)
在Pure 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();
}
答案 3 :(得分: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));
}
答案 4 :(得分:2)
我的方式:我用spring定义一个自定义Scenario-Scope 每一个新的场景都会有一个新的背景
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();
}
}
答案 5 :(得分:1)
如果您正在使用黄瓜的Serenity框架,您可以使用当前会话。
splice
http://thucydides-webtests.com/2012/02/22/managing-state-between-steps/中有关此功能的更多信息。 (Serenity之前被称为Thucydides)
答案 6 :(得分:1)
其他选项是使用ThreadLocal存储。创建上下文映射并将其添加到地图中。 Cucumber JVM运行同一个线程中的所有步骤,您可以跨所有步骤访问它。为了简化操作,您可以在挂钩之前实例化存储,并在挂钩后清除。