salida - paso de parametros en java ejemplo
Buena práctica para pasar variables entre pasos de pepino-jvm. (7)
Para pasar variables entre pasos ahora estoy haciendo algo como el ejemplo de la siguiente manera:
Feature: Demo
Scenario: Create user
Given User creation form management
When Create user with name "TEST"
Then User is created successfully
Clase de Java con definiciones de pasos:
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
}
Mi pregunta es si esta es una buena práctica para compartir información entre pasos. O sería mejor definir la característica como:
Then User with name "TEST" is created successfully
Soy nuevo con cucumber-jvm, lo siento si es una pregunta sin cerebro.
Cualquier ayuda sería apreciada. Gracias
A mi manera: defino un Scenario-Scope personalizado con cada nuevo escenario, habrá un nuevo contexto.
Feature @Dummy
Scenario: zweites Scenario
When Eins
Then Zwei
1: usar primavera
<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: construir clase de alcance personalizado
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: uso en 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 {
}
la clase de alcance
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();
}
}
En Pure Java, solo uso un objeto Singleton que se crea una vez y se borra después de las pruebas.
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();
}
Está bien compartir datos entre los pasos definidos dentro de una clase usando una variable de instancia. Si necesita compartir datos entre los pasos en diferentes clases, debe mirar las integraciones DI (PicoContainer es el más simple).
En el ejemplo que muestra, me gustaría preguntar si es necesario mostrar "PRUEBA" en el escenario. El hecho de que el usuario se llame PRUEBA es un detalle incidental y hace que el escenario sea menos legible. ¿Por qué no generar un nombre aleatorio (o algo duro) en Create_user_with_name ()?
Otra opción es utilizar el almacenamiento ThreadLocal. Crea un mapa de contexto y agrégalos al mapa. Cucumber JVM ejecuta todos los pasos en el mismo subproceso y usted tiene acceso a todos estos pasos. Para hacerlo más fácil, puede crear una instancia del almacenamiento antes del enganche y borrar después del enganche.
Para compartir puntos en común entre los pasos, necesitas usar un World . En Java no es tan claro como en Ruby.
Citando al creador del pepino.
El propósito de un "Mundo" es doble:
1) Aislar el estado entre escenarios.
2) Comparta datos entre definiciones de pasos y enlaces dentro de un escenario.
Cómo se implementa esto es específico del lenguaje. Por ejemplo, en ruby, la
self
variable implícita dentro de una definición de paso apunta al objeto Mundo del escenario actual. Esta es una instancia de Object por defecto, pero puede ser lo que quieras si usas el gancho del mundo.En Java, tienes muchos objetos del mundo (posiblemente conectados).
El equivalente de World in Cucumber-Java es todos los objetos con anotaciones hook o stepdef . En otras palabras, cualquier clase con métodos anotados con @Before, @After, @Given, etc., se instanciará exactamente una vez para cada escenario.
Esto logra el primer objetivo. Para lograr el segundo objetivo tienes dos enfoques:
a) Use una sola clase para todas sus definiciones de pasos y enlaces
b) Use varias clases divididas por responsabilidad [1] y use la inyección de dependencia [2] para conectarlas entre sí.
La opción a) se rompe rápidamente porque su código de definición de paso se convierte en un desastre. Es por eso que la gente tiende a usar b).
[1] https://github.com/cucumber/cucumber/wiki/Step-Organization
[2] PicoContainer, Spring, Guice, Weld, OpenEJB, Needle
Los módulos de inyección de dependencia disponibles son:
- pepino-picocontainer
- pepino-guice
- pepino-openejb
- pepino de primavera
- soldadura de pepino
- aguja de pepino
Publicación original aquí https://groups.google.com/forum/#!topic/cukes/8ugcVreXP0Y .
Espero que esto ayude.
Si está utilizando el marco de Serenity con Pepino, puede usar la sesión actual.
Serenity.getCurrentSession()
más sobre esta característica en http://thucydides-webtests.com/2012/02/22/managing-state-between-steps/ . (La serenidad se llamaba Tucídides antes)
Yo diría que hay razones para compartir información entre pasos, pero no creo que ese sea el caso en este escenario. Si propaga el nombre de usuario a través de los pasos de prueba, entonces no está claro de qué se trata. Creo que es mejor decir específicamente en el escenario lo que se espera. Probablemente haría algo como esto:
Feature: Demo
Scenario: Create user
Given User creation form management
When Create user with name "TEST"
Then A user named "TEST" has been created
Entonces, sus pasos de prueba reales podrían verse algo como:
@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));
}