Cucumber-js: 从步骤定义调用步骤

创建于 2011-06-01  ·  31评论  ·  资料来源: cucumber/cucumber-js

最有用的评论

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

从 stepdefs 调用步骤是我希望我永远不会添加到 Cucumber(-Ruby) 中的功能之一,因为它为人们提供了很多可以吊死自己的绳索。 这是因为 Ruby stepdefs 使用了你不能从其他地方调用的匿名闭包(除非你通过箍)。

使用 JavaScript,情况就不同了。 步骤定义使用一流的功能!

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

所有31条评论

实现此功能后,请记住,我们计划弃用除#steps之外的所有内容,以便在 cucumber-rb 中执行此操作。 见https://github.com/cucumber/cucumber/issues/68

谢谢马特。 -js 将仅支持 steps() 。

我喜欢!

这方面有什么进展吗? 似乎是一个非常重要的功能。

这不是当前里程碑 (0.3) 的一部分。 它_应该_是 0.4 的一部分。

@mattwynne我想我们也想支持step() 。 我对么?

@jbpros我猜。 也许你可以从#step开始。 实现起来更简单,因为您只是调用一个步骤而不是解析 Gherkin。

我个人很乐意使用没有此功能的 Cucumber,我从不使用它并认为它是不好的做法。 我总是更喜欢委托给方法。

最终,如果我们要支持这一点,我更希望看到它在 Gherkin 中实现,因此您可以定义一个映射到一堆其他低级步骤的宏步骤。 然后 Cucumber 会被告知调用较低级别的,并且几乎不需要知道是否有任何映射正在进行。 那将是我的偏好。

TL;DR:我们真的应该将steps() / step()添加到 Cucumber.js(和 -jvm、-ruby 2 等)吗?

我完全同意你,马特。 _不幸的是_,这是目前 Cucumber.js 上最需要的功能。

据我了解,许多人将步骤定义视为_方法_或_功能_。 在我看来,它们是一些流利的语言句子和编程语言代码之间的映射,仅此而已。 这对我们如何对待这些野兽有着深远的影响。

从程序员的角度来看,步骤定义看起来就像方法一样。 我认为这是今天 Cucumber_s_ 的一个弱点。 步骤定义不应该作为 API 公开,而是作为一个显式的翻译映射、一个字典,可以这么说。

@msassak已经对此产生了有趣的想法,我认为他在 Cucumber 2.0 中重新建模那些“映射”做得很好。

我不得不说我现在不愿意在 Cucumber.js 中解决这个问题。 另一方面,我不想仅仅因为我的个人品味/意见而进行功能保留。

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

从 stepdefs 调用步骤是我希望我永远不会添加到 Cucumber(-Ruby) 中的功能之一,因为它为人们提供了很多可以吊死自己的绳索。 这是因为 Ruby stepdefs 使用了你不能从其他地方调用的匿名闭包(除非你通过箍)。

使用 JavaScript,情况就不同了。 步骤定义使用一流的功能!

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) :锤子:

我不确定我是否理解step()会比这里的简单 JS 函数调用更好。 无论如何,这不就是它最终会做的事情,还有一个额外的间接层(即,向仍需要转换为 JS 函数的特定用户发出 GET 请求的步骤定义)?

我注意到您写了_因为我无法触发场景_,您的意思是步骤还是故意的场景(在后一种情况下,我认为我可以看到您要做什么)。

您仍然可以在后台定义用户并在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 | ...  |

是的,这意味着将请求响应存储在一个数组中并对其进行迭代。 有那么糟糕吗?

好吧,如果您有一些流程(例如结帐流程)并且您想进入最终确认页面,您可以通过在步骤定义中(某处)定义的步骤。
我同意您也可以在某处定义一个函数并从步骤定义中调用它。 但它最大限度地减少了将其从步骤转移到功能的努力。 如果您在 BDD 中的某处描述了一些流程,则无需花费额外的时间将其移动到单独的库中,您可以简单地调用步骤定义。

调用一个步骤几乎是无用的。 我正在考虑在这里移植所有 ruby​​ 的 step(s) 功能。 但随着我的请求结束,我不会花时间去做。

谢谢。

很遗憾,这无法解决,正如@cono所说:调用一个步骤几乎是无用的,实际案例是更复杂的操作。

这对于创建一些细粒度的场景然后其他场景重复此操作非常有帮助。 特别是当步骤定义在多个文件中时,在这种情况下,功能重用替代方案不太容易也不干净。

嗨! 我创建了一个完全符合您要求的库(从另一个步骤调用一个步骤),请看这里: https ://github.com/hackhat/cucumberry
欢迎反馈!

@hackhat看起来真的很酷。 我通常喜欢步骤定义的同步部分。 cucumber-pro 只是 cucumber.js 的插件吗?

@jlin412我真的不知道怎么打电话,但就像黄瓜的帮手一样。 感谢您的反馈

@hackhat为了创建同步步骤,我需要使用语法:this.addStep(...)? 我是否也需要使用 selenium-sync 而不是 protractor.js/webdriver.js?

@jlin412你只能使用同步插件,看文档怎么做。 我在手机上,所以我不能给你确切的步骤。 如果您可以等待,我会在葡萄牙时间 23 小时 30 分左右更好地解释。

@hackhat请将您的项目名称更改为其他名称。 见 hackhat/cucumber-pro#1

@aslakhellesoy将名称更改为https://github.com/hackhat/cucumberry

@aslakhellesoy那么如何链接步骤调用? 喜欢跟风?

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

这不是很好,因为x_is_something会在y_is_something有机会完成它的工作之前调用回调。

而且,如果 step 在世界上下文中存储变量,每次调用函数都会结束,我们需要像这样绑定它:

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

有人有解决这些问题的方法吗?

您需要使用异步并使用并行功能。 这样你打电话
仅在两个子调用都完成后才主 cb。
关于绑定,您可以将其与绑定一起使用,也可以使用闭包变量。

2015 年 5 月 14 日星期四 00:15,云嘉通知@github.com 写道:

@aslakhellesoy https://github.com/aslakhellesoy那么如何链接
步骤调用? 喜欢跟风?

函数 x_is_something(x, cb) {
console.log('x', x);
这个.x = x;
CB();
}函数y_is_something(y,cb){
console.log('y', y);
这个.y = y;
CB();
}

给定(/super z/,函数(cb){
x_is_something(97, cb);
y_is_something(8, cb);
});

这不能很好地工作,因为 x_is_something 会调用
在 y_is_something 有机会完成其工作之前回调。

而且,如果 step 在世界上下文中存储变量,它将结束
每次调用该函数,我们都需要像这样绑定它:

给定(/super z/,函数(cb){
x_is_something.bind(this)(97, cb);
y_is_something.bind(this)(8, cb);
});

有人有解决这些问题的方法吗?


直接回复此邮件或在 GitHub 上查看
https://github.com/cucumber/cucumber-js/issues/11#issuecomment -101845619
.

+1,这应该是库的一部分。

@mattwynne的建议(添加支持代码可重用性的小黄瓜功能):

    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
        ...

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

@aslakhellesoy的建议(将重复的代码提取到 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)
        ...

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

从步骤定义调用步骤

    When a
    Then x
    When b
    Then y

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

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

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

我还是不明白为什么我们需要另一个不必要的抽象级别,你有什么解释吗?

@yunjia这是基本的回调理论:

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

至于绑定问题,您应该将这些函数定义为World上的方法:

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;

然后在您的步骤定义中:

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

另外,请注意 Cucumber.js 0.5+ 支持同步步骤定义:

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

@inf3rno步骤定义是纯英语和 JS 代码之间的一个薄翻译层。 通过允许从步骤调用步骤,我们使该层更胖。 步骤之间变得相互耦合,使它们极难维护。

它还鼓励人们将 Gherkin 用作脚本语言,但它根本不是。

@inf3rno如果您想重用 stepdefs 中的代码,请将 stepdef 的主体移动到常规 javascript 函数并重用它。

@jbpros

它还鼓励人们将 Gherkin 用作脚本语言,但它根本不是。

你能详细说明一下吗? 我不明白有什么联系,因为步骤定义不在小黄瓜中,只有文本模式是相似的。

@inf3rno如果您可以从步骤中调用步骤,那么您将再次跳回到“Gherkinland”:需要解析步骤名称,并与可以执行的步骤定义相匹配。 您基本上是在 gherkin 中编写 gherkin 脚本(它们隐藏在 JS 步骤定义中,但这是一个使维护 POV 变得更糟的细节)。

@aslakhellesoy @jbpros这个想法是步骤应该是代数的、可组合的类型,但事实并非如此。

@jbpros ,我将使用您的解决方案,因为我习惯于使用 Java 编程并且不担心承诺 :)

有没有人想出一个黄瓜的扩展来定义其他步骤的步骤? 就像是

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

我正在思考这是否是一个有用的扩展,可以更好地描述通过系统的长期用户旅程,并且更愿意使用任何现有技术。

该线程已被自动锁定,因为它关闭后没有任何最近的活动。 请针对相关错误打开一个新问题。

此页面是否有帮助?
0 / 5 - 0 等级