Buona pratica per passare le variabili tra i passi cetriolo-jvm

Per passare le variabili tra i passaggi ora sto facendo qualcosa di simile all’esempio come segue:

Feature: Demo Scenario: Create user Given User creation form management When Create user with name "TEST" Then User is created successfully 

Classe Java con definizioni dei passi:

 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 } 

La mia domanda è se questa è una buona pratica per condividere le informazioni tra i passaggi? O sarebbe meglio definire la funzionalità come:

 Then User with name "TEST" is created successfully 

Sono nuovo con cetriolo-jvm, mi dispiace se è una domanda senza cervello.

Qualsiasi aiuto sarebbe apprezzato. Grazie

    Per condividere punti in comune tra i passaggi è necessario utilizzare un mondo . In Java non è così chiaro come in Ruby.

    Citando il creatore di Cucumber.

    Lo scopo di un “mondo” è duplice:

    1) Isolare lo stato tra gli scenari.

    2) Condividere i dati tra le definizioni dei passaggi e gli hook all’interno di uno scenario.

    Come questo è implementato è linguaggio specifico. Ad esempio, in ruby, la variabile self implicita all’interno di una definizione di passo punta all’object Mondo dello scenario corrente. Questa è per impostazione predefinita un’istanza di Object, ma può essere qualsiasi cosa tu voglia usare l’hook World.

    In Java, ci sono molti oggetti del mondo (possibilmente connessi).

    L’equivalente del mondo in Cetriolo-Java sono tutti gli oggetti con annotazioni hook o stepdef . In altre parole, qualsiasi class con metodi annotati con @Before, @After, @Given e così via verrà istanziata esattamente una volta per ogni scenario.

    Questo raggiunge il primo objective. Per raggiungere il secondo objective hai due approcci:

    a) Utilizzare una singola class per tutte le definizioni di passo e gli hook

    b) Utilizzare diverse classi divise per responsabilità [1] e utilizzare dependency injection [2] per collegarle tra loro.

    Opzione a) Si interrompe rapidamente perché il codice di definizione del passo diventa un disastro. Ecco perché le persone tendono ad usare b).

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

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

    I moduli di Iniezione di dipendenza disponibili sono:

    • cetriolo-picocontainer
    • cetriolo-Guice
    • cetriolo-OpenEJB
    • cetriolo-molla
    • cetriolo-saldatura
    • cetriolo-ago

    Post originale qui https://groups.google.com/forum/#!topic/cukes/8ugcVreXP0Y .

    Spero che questo ti aiuti.

    Va bene condividere i dati tra i passaggi definiti all’interno di una class utilizzando una variabile di istanza. Se è necessario condividere i dati tra i passaggi di classi diverse, è necessario esaminare le integrazioni DI (PicoContainer è il più semplice).

    Nell’esempio che mostri, chiederei se mostrare “TEST” nello scenario sia assolutamente necessario. Il fatto che l’utente sia chiamato TEST è un dettaglio accessorio e rende lo scenario meno leggibile. Perché non generare un nome casuale (o codice hard qualcosa) in Create_user_with_name ()?

    In Pure java, io uso solo un object Singleton che viene creato una volta e cancellato dopo i test.

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

    Direi che ci sono dei motivi per condividere le informazioni tra i passaggi, ma non penso che sia il caso in questo scenario. Se si diffonde il nome utente tramite i passaggi del test, non è chiaro dalla funzione cosa sta succedendo. Penso che sia meglio dire nello specifico che cosa è previsto. Probabilmente farei qualcosa del genere:

     Feature: Demo Scenario: Create user Given User creation form management When Create user with name "TEST" Then A user named "TEST" has been created 

    Quindi, i tuoi passaggi di test effettivi potrebbero essere simili a:

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

    Ecco la mia strada: definisco uno Scenario-Ambito personalizzato con la spring ogni nuovo scenario ci sarà un nuovo contesto

     Feature @Dummy Scenario: zweites Scenario When Eins Then Zwei 

    1: usa la molla

      1.2.5 4.12    info.cukes cucumber-java ${cucumber.version} test   info.cukes cucumber-junit ${cucumber.version} test   junit junit ${junit.version} test   info.cukes cucumber-spring ${cucumber.version} test     org.springframework spring-test 4.3.4.RELEASE test   org.springframework spring-context 4.3.4.RELEASE test   org.springframework spring-tx 4.3.4.RELEASE test   org.springframework spring-core 4.3.4.RELEASE test   commons-logging commons-logging     org.springframework spring-beans 4.3.4.RELEASE test   org.springframework.ws spring-ws-core 2.4.0.RELEASE test  

    2: crea una class di ambito personalizzata

     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: utilizzo in stepdef

     @ContextConfiguration(classs = { 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 class di ambito

     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 objectMap = Collections.synchronizedMap(new HashMap()); /** (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(); } } 

    Se stai usando il framework Serenity con cetriolo puoi usare la sessione corrente.

     Serenity.getCurrentSession() 

    altro su questa funzione in http://thucydides-webtests.com/2012/02/22/managing-state-between-steps/ . (Serenity era chiamato Thucydides prima)

    Un’altra opzione è usare lo storage ThreadLocal. Crea una mappa di contesto e aggiungili alla mappa. Cucumber JVM esegue tutti i passaggi nello stesso thread e si ha accesso a ciò attraverso tutti i passaggi. Per semplificare, puoi istanziare l’archiviazione prima del hook e svuotare dopo il hook.