Cucumber-js: Llamar pasos desde definiciones de pasos

Creado en 1 jun. 2011  ·  31Comentarios  ·  Fuente: cucumber/cucumber-js

Comentario más útil

:-1: :-1: :-1: :-1: :-1:

Llamar a pasos desde stepdefs es una de esas características que desearía no haber agregado nunca a Cucumber (-Ruby), porque proporciona mucha cuerda para que las personas se cuelguen. Llegó a ser porque Ruby stepdefs usa cierres anónimos que no puede llamar desde otro lugar (a menos que pase por aros).

Con JavaScript es una situación diferente; ¡Las definiciones de pasos utilizan funciones de primera clase!

function x_is_something(x, cb) {
  console.log('x', x);
  cb();
}

Given(/x is (.*)/, x_is_something);

Given(/super x/, function(cb) {
  x_is_something(97, cb);
});

Todos 31 comentarios

Cuando esto se implemente, tenga en cuenta que planeamos desaprobar todo lo que no sea #steps por hacer esto en cucumber-rb. Consulte https://github.com/cucumber/cucumber/issues/68

gracias matt -js solo admitirá pasos () entonces.

¡Me gusta!

¿Algún progreso en esto? Parece una característica bastante vital.

Esto no está planificado como parte del hito actual (0.3). _debería_ ser parte de 0.4.

@mattwynne Supongo que también queremos apoyar a step() . ¿Estoy en lo correcto?

@jbpros , supongo. Tal vez podrías comenzar con #step . Es más simple de implementar porque solo está invocando un paso en lugar de analizar Gherkin.

Personalmente, estaría feliz de usar un Cucumber sin esta característica, nunca lo uso y lo considero una mala práctica. Siempre prefiero delegar en métodos.

En última instancia, si vamos a respaldar esto, preferiría verlo implementado en Gherkin, para que tenga una forma de definir un paso macro que se asigne a un montón de otros pasos de bajo nivel. Luego, a Cucumber solo se le dice que invoque a los de nivel inferior y apenas necesita saber que hay algún mapeo en curso. Esa sería mi preferencia.

TL;DR: ¿Deberíamos realmente agregar steps() / step() a Cucumber.js (y -jvm, -ruby 2, etc.)?

Totalmente de acuerdo contigo Matt. _Desafortunadamente_, esta es la característica más buscada en este momento en Cucumber.js.

Según tengo entendido, muchas personas consideran las definiciones de pasos como _métodos_ o _funciones_. A mi modo de ver, son asignaciones entre algunas oraciones de lenguaje fluido y fragmentos de código de lenguaje de programación, nada más. Esto tiene profundas implicaciones en cuanto a cómo tratamos a esas bestias.

Sin embargo, desde la perspectiva del programador, las definiciones de pasos parecen métodos. Lo veo como una debilidad en Cucumber_s_ hoy. Las definiciones de pasos no deben exponerse como una API, sino como un mapa de traducción explícito, un diccionario, por así decirlo.

@msassak ya tenía pensamientos interesantes sobre esto y creo que hizo un gran trabajo remodelando esos "mapeos" en Cucumber 2.0.

Debo decir que soy reacio a trabajar en este mismo problema en Cucumber.js en este momento. Por otro lado, no quiero hacer retención de funciones solo por mis gustos/opiniones personales.

:-1: :-1: :-1: :-1: :-1:

Llamar a pasos desde stepdefs es una de esas características que desearía no haber agregado nunca a Cucumber (-Ruby), porque proporciona mucha cuerda para que las personas se cuelguen. Llegó a ser porque Ruby stepdefs usa cierres anónimos que no puede llamar desde otro lugar (a menos que pase por aros).

Con JavaScript es una situación diferente; ¡Las definiciones de pasos utilizan funciones de primera clase!

function x_is_something(x, cb) {
  console.log('x', x);
  cb();
}

Given(/x is (.*)/, x_is_something);

Given(/super x/, function(cb) {
  x_is_something(97, cb);
});

CLOSED (WONTFIX) :martillo:

No estoy seguro de entender cómo step() sería mejor que una simple llamada de función JS aquí. ¿No es eso lo que terminaría haciendo de todos modos, con una capa adicional de direccionamiento indirecto (es decir, una definición de paso para realizar una solicitud GET a un usuario específico que aún debe traducirse a una función JS)?

Me di cuenta de que escribiste _ya que no tengo forma de activar un escenario_, ¿quisiste decir paso o es ese escenario intencional (en el último caso, puedo ver lo que estás tratando de hacer, creo).

Todavía puede definir usuarios en segundo plano e iterar sobre ellos en su paso When .

Feature:
  Background:
    Given a valid user called Simon
    And a valid user called Sarah
    And a valid user called Johnny

  Scenario Outline:
    When each valid user sends a GET request to /search<query>
    Then everyone's request response code is 400
    And everyone's request response body starts with <body>

  Examples:
    | query  | body |
    | ?foo=1 | ...  |

Y sí, esto significa almacenar las respuestas de las solicitudes en una matriz e iterar sobre ellas. ¿Es tan malo?

Bueno, si tiene algún flujo (por ejemplo, flujo de pago) y desea llegar a la página de confirmación final, puede seguir los pasos, definidos (en algún lugar) en las definiciones de pasos.
Acepto que también puede definir una función en algún lugar e invocarla desde la definición del paso. Pero minimiza el esfuerzo de moverlo de los pasos a la función. Si describió algún flujo en algún lugar de BDD, no necesita dedicar tiempo adicional para moverlo a una biblioteca separada, simplemente puede invocar definiciones de pasos.

Invocar un solo paso es casi inútil. Estaba pensando en portar aquí la funcionalidad de todos los pasos de Ruby. Pero como mi solicitud se cerró, no le dedicaría tiempo.

Gracias.

Tan triste que esto no se solucionará, como dice @cono : invocar un solo paso es casi inútil, los casos reales son operaciones más complejas.

Esto sería realmente útil para crear algunos escenarios detallados y luego los otros escenarios que repiten estas operaciones. Especialmente cuando los pasos están definidos en más de un archivo, en ese caso la alternativa de reutilización de funciones no es fácil ni limpia.

¡Hola! Creé una librería que hace exactamente lo que estás pidiendo (llama un paso desde otro paso), échale un vistazo aquí: https://github.com/hackhat/cucumberry
¡Los comentarios son bienvenidos!

@hackhat Se ve muy bien. Por lo general, me gusta la parte de sincronización de la definición de pasos. ¿Cucumber-pro es solo un complemento para cucumber.js?

@ jlin412 Realmente no sé cómo llamar, pero es como un ayudante para pepino. Gracias por la retroalimentación

@hackhat Para crear un paso de sincronización, necesitaré usar la sintaxis: this.addStep(...)? ¿Tendré que usar selenium-sync también en lugar de protractor.js/webdriver.js?

@ jlin412 solo puede usar el complemento de sincronización, consulte los documentos sobre cómo hacerlo. Estoy en el móvil, así que no puedo darte los pasos exactos. Si puedes esperar te explico mejor alrededor de las 23h30 hora de Portugal.

@hackhat , cambie el nombre de su proyecto a otro. Ver hackhat/cucumber-pro#1

@aslakhellesoy Cambió el nombre a https://github.com/hackhat/cucumberry

@aslakhellesoy Entonces, ¿cómo encadenar las llamadas de paso? ¿Te gusta seguir?

function x_is_something(x, cb) {
  console.log('x', x);
  this.x = x;
  cb();
}
function y_is_something(y, cb) {
  console.log('y', y);
  this.y = y;
  cb();
}

Given(/super z/, function(cb) {
  x_is_something(97, cb);
  y_is_something(8, cb);
});

Eso no funciona muy bien ya que x_is_something habría llamado a la devolución de llamada antes de que y_is_something tuviera la oportunidad de terminar su trabajo.

Y también, si el paso almacena la variable en el contexto mundial, terminará cada vez que llame a la función, debemos vincularla así:

Given(/super z/, function(cb) {
  x_is_something.bind(this)(97, cb);
  y_is_something.bind(this)(8, cb);
});

Alguien tuvo soluciones a estos problemas?

Necesita usar async y usar la función paralela. De esta manera llamas
el cb principal solo después de que ambas llamadas secundarias hayan terminado.
Acerca del enlace, puede usar esto con enlace o usar una variable de cierre.

El jueves, 14 de mayo de 2015, 00:15 Yun Jia [email protected] escribió:

@aslakhellesoy https://github.com/aslakhellesoy Entonces cómo encadenar el
llamadas de paso? ¿Te gusta seguir?

función x_es_algo(x, cb) {
consola.log('x', x);
esto.x = x;
cb();
}función y_es_algo(y, cb) {
consola.log('y', y);
esto.y = y;
cb();
}

Dado(/super z/, función(cb) {
x_es_algo(97, cb);
y_es_algo(8, cb);
});

Eso no funciona muy bien ya que x_is_something habría llamado al
devolución de llamada antes de que y_is_something tenga la oportunidad de terminar su trabajo.

Y también, si el paso almacena la variable en el contexto mundial, terminará
cada vez que llamamos a la función, necesitamos enlazarla como:

Dado(/super z/, función(cb) {
x_es_algo.bind(esto)(97, cb);
y_es_algo.bind(esto)(8, cb);
});

Alguien tuvo soluciones a estos problemas?


Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/cucumber/cucumber-js/issues/11#issuecomment-101845619
.

+1, esto debería ser parte de la biblioteca.

Lo que sugirió @mattwynne (agregue una función de pepinillo que admita la reutilización de código):

    When a
    Then x
    When b
    Then y

---------------------------------

    Define x
        Then p
        And y
        And q

---------------------------------

    Step a
        ...
    Step b
        ...
    Step p
        ...
    Step q
        ...
    Step y
        ...

---------------------------------

Lo que sugirió @aslakhellesoy (extrayendo el código duplicado a las funciones js):

    When a
    Then x
    When b
    Then y

---------------------------------

    Step a
        ...
    Step b
        ...
    Step x
        ...
        Call f(p)
        ...
    Step y
        Call f(p)

---------------------------------

    Function f(p)
        ...

---------------------------------

Llamar pasos desde definiciones de pasos

    When a
    Then x
    When b
    Then y

---------------------------------

    Step a
        ...
    Step b
        ...
    Step x
        ...
        Call y(p)
        ...
    Step y
        ...

---------------------------------

Todavía no entiendo por qué necesitamos otro nivel de abstracción innecesario, ¿tienes alguna explicación?

@yunjia esta es la teoría básica de devolución de llamada:

Given(/super z/, function(cb) {
  x_is_something(97, function () {
    y_is_something(8, cb);
  });
});

En cuanto al problema de vinculación, debe definir esas funciones como métodos en su mundo :

function World(callback) {
    this.x_is_something = function (x, callback) {
      this.x = ...
    };

    this.y_is_something = function (y, callback) {
      this.y = ...
    };

    callback(); // tell Cucumber we're finished and to use 'this' as the world instance
  };
}
module.exports.World = World;

Luego, en sus definiciones de pasos:

Given(/super z/, function(cb) {
  var self = this;
  self.x_is_something(97, function () {
    self.y_is_something(8, cb);
  });
});

Además, tenga en cuenta que las definiciones de pasos sincrónicos son compatibles con Cucumber.js 0.5+:

Given(/super z/, function() {
  this.x_is_something(97);
  this.y_is_something(8);
});

Las definiciones de pasos de @inf3rno son una fina capa de traducción entre el inglés simple y el código JS. Al permitir llamar pasos a pasos, hacemos que esa capa sea más gruesa. Los escalones se acoplan entre sí, haciéndolos extremadamente difíciles de mantener.

También alienta a las personas a usar Gherkin como un lenguaje de secuencias de comandos, que no lo es en absoluto.

@inf3rno si desea reutilizar código en stepdefs, mueva el cuerpo de stepdef a una función javascript normal y reutilícelo.

@jbpros

También alienta a las personas a usar Gherkin como un lenguaje de secuencias de comandos, que no lo es en absoluto.

¿Puedes elaborar por favor? No entiendo cuál es la conexión, ya que las definiciones de pasos no están en pepinillo, solo el patrón de texto es similar.

@ inf3rno si puede llamar a los pasos de los pasos, está saltando de nuevo a "Gherkinland": los nombres de los pasos deben analizarse y compararse con las definiciones de los pasos que se pueden ejecutar. Básicamente, está escribiendo scripts de pepinillo dentro de pepinillo (están ocultos dentro de las definiciones de pasos de JS, pero ese es un detalle que lo empeora aún más desde un punto de vista de mantenimiento).

@aslakhellesoy @jbpros La idea es que los pasos deben ser de tipo algebraico componible, que no lo es.

@jbpros , voy a usar su solución ya que estoy acostumbrado a programar en Java y no me preocupo por las promesas :)

¿Alguien alguna vez ideó una extensión para pepino para definir pasos en términos de otros pasos? Algo como

Understand I log in to {site} as {user}, {pass}
    I visit {site}
    I log in as {user}, {pass}

Estoy pensando si esta es una extensión útil para describir mejor los viajes largos del usuario a través del sistema y preferiría trabajar con cualquier técnica anterior.

Este hilo se ha bloqueado automáticamente ya que no ha habido ninguna actividad reciente después de que se cerró. Abra un nuevo problema para los errores relacionados.

¿Fue útil esta página
0 / 5 - 0 calificaciones