Cucumber-js: 功能请求:不使用 `this` 关键字的 UI

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

给定一个世界,例如:

require('cucumber').defineSupportCode( ({setWorldConstructor:world}) => {
    world(class { 
      constructor(p) { ... }
      foo(bar) { return new Promise(.... ) }
    })
})

我希望能够写:

  When(/^I do the foo with (.*)$/i, (ctx, bar) => ctx.foo(bar) );

代替

  When(/^I do the foo with (.*)$/i, function(bar) {
      return this.foo(bar)
  });

这也将使我免于在所有异步操作中使用.bind(this) ,或者如果您愿意 - 摆脱与var that = this的混淆。

还有一个好处(恕我直言)-它将帮助人们摆脱世界的继承树,并使他们更好地适应 JS 的强大功能

accepted enhancement

最有用的评论

好的。 由于不是每个人都是函数式程序员,并且为了防止引入破坏性更改,你们如何看待使世界作为第一个参数传递的选项?

defineSupportCode(({setWorldInjectionStrategy}) => {
  setWorldInjectionStrategy('this') // default
  setWorldInjectionStrategy('argument') // makes world be the first argument to steps and hooks 
})

所有16条评论

我同意这意味着您始终接受world作为参数,这是 API 更改。
我认为像摩卡咖啡那样有--ui exports--ui tdd--ui bdd - ccuue 也可以。
它基本上是一个项目变量,用于确定是否在世界上调用/应用步骤,或将其作为第一个参数接受。

从长远来看,我真的认为这不值得。 是的,这对于您可以使用=>那些 1 行步骤很好,否则您将无法使用。 根据我的经验,我很少有这样的步骤。 我很惊讶 ES6 没有包含一个不保留上下文的箭头函数,因为这对我来说是理想的。

这也将使我免于在所有异步操作中使用.bind(this) ,或者如果您愿意 - 摆脱与var that = this的混淆。

您不能在步骤定义中使用粗箭头来避免这种情况吗?

还有一个好处(恕我直言)-它将帮助人们摆脱世界的继承树,并使他们更好地适应 JS 的强大功能

你能再解释一下吗/举个例子

简而言之:我属于一个程序员学校,他们避免将上下文作为一个类来表达。 越多的人接触到 OOP 的阴暗面,这所学校就越大——JS 社区中阻止箭头函数保留上下文的运动就是这种运动力量的一个例子。

没有进入宗教辩论 - 我只想说我相信生活并让生活,你想使用课程吗? 伟大的。 不要强迫我:-)

我的总体感觉是,这可能是一个小小的改变,如果您能向我提供您认为最好的方法的总体方向,我将很乐意这样做;-)

当我们定义步骤时,我们定义的是函数,而不是类,因此逻辑上this在其中没有位置。

我认为这是 FP 范式而不是 OOP 范式的情况。

@osher我认为我们可以让@charlierudolph怀疑他不想强迫您使用类。 也就是说,他不应该被迫在他使用这个包的任何地方集成(破坏)API 更改,除非它提供了一些切实的优势。

@charlierudolph我认为“JS 的强大功能”

恕我直言,此更改将使步骤定义更简单,更合乎逻辑。 它基本上只是删除这一行: const thisWorldNotThisStep = this

绝对没有必要将.bind(this)用于所有异步函数,只要在步骤定义中使用箭头函数,就不需要使用const self = this模式。

问题(不是大问题)是在步骤定义中, this指的是世界,而不是步骤,这是违反直觉的。

好的。 由于不是每个人都是函数式程序员,并且为了防止引入破坏性更改,你们如何看待使世界作为第一个参数传递的选项?

defineSupportCode(({setWorldInjectionStrategy}) => {
  setWorldInjectionStrategy('this') // default
  setWorldInjectionStrategy('argument') // makes world be the first argument to steps and hooks 
})

我完全同意注入世界/上下文具有步骤参数的想法。 从这个项目开始,整个 JS 生态系统都在转向完全接受 ES6 允许的新功能,这是一件好事❤️

如果您查看 Express 或 Koa 等其他工具,它们会将当前请求上下文设置为this并作为中间件的第一个参数(相当于黄瓜步骤)。 该解决方案允许传统的函数使用和 ES6 箭头函数的使用。
作为第一个参数的上下文的另一个优点是

对于@charlierudolph提出的解决方案,我认为从长远来看这行不通:这会将社区一分为二。 并非所有黄瓜示例都适用于所有安装,并且文档将一分为二。 不花哨。

我不确定对重大变更的担忧,v2 是引入这种变更的最佳时机。 等待将强制制作/等待另一个专业。

我们如何改变BeforeAfter来注入world

defineSupportCode(function({After, Before, Given}) {
  let world;

  // Asynchronous Callback
  Before(function (w, scenarioResult, callback) {
    world = w;
    callback();
  });
  After(function (world, scenarioResult, callback) {
    callback();
  });

  Given('I access the world', () => {
    assert(world); // Yay!
  });
});

这样,你可以捕捉到world的变量的一个before一步。 它不会弄乱每个步骤的定义。

接下来,我们保持旧的工作方式,但生成deprecated消息。 或者甚至不这样做,只需将选项留给想要使用该方法并知道他们在做什么的人。

恐怕网上找到的片段兼容性的论点是一个令人信服的论点。

即使会有--ui标志,或setWorldInjectionStrategy('argument') - 它成为一个问题,可以更好地作为主要版本的重大更改进行交流,并提供所有在线讨论和与此类更改相称的骚动,并帮助消除混乱。

所以我投票支持在下一个版本的 cuke 中做这件事,并且......在它发布之前做。

金丝雀版本或隐藏标志将是一个很棒的促销活动,我会在早期使用和反馈

我很想公开一个替代的 FP 接口。

下面呢? (使用 async/await 来说明它也是对 promise 友好的)

import {initialize, Given, Before} from 'cucumber/fn'

// specify a function that returns the initial context:
initialize(async () => ({ a: 42 }))

Before({ timeout: 10 }, async (ctx) => {
  await doStuff(ctx.a)
})

Given(/^a step passes with {number}$/, async (ctx, number) => {
  const newA = await computeStuff(ctx.a, number)
  // tell cucumber about the new context:
  return Object.assign({}, ctx, { a: newA })
})

我已经破解了提供功能步骤定义的黄瓜-fp 。 我已经有了一些改进的想法。 欢迎反馈!

我建议我们关闭这个问题,让人们试验那个小库。 也许有一天我们可以将其引入 Cucumber.js。

@jbpro我不想再安装另一个依赖项,这样我就可以在测试中使用箭头函数。

对于每个人来说,这实际上应该很容易实现。 以下代码片段对我来说效果很好(在功能上),但有一个很大的警告使其无法使用:

// Don't rely on `this` in step definitions. It's 2021 for crying out loud.
const definitionFunctionWrapper = (fn) =>
    function(...args) {
        return fn(...args.slice(0, -1), this);
    }

需要注意的是,由于附加参数,每个步骤定义现在都会记录以下错误:

    Error: function uses multiple asynchronous interfaces: callback and promise
       to use the callback interface: do not return a promise
       to use the promise interface: remove the last argument to the function

如果您尝试添加其他参数来解决它,您会得到

function has 3 arguments, should have 1 (if synchronous or returning a promise) or 2 (if accepting a callback)

Cucumber-js 所需要的只是一个禁用函数参数检查的选项,这个问题就会消失。 实际上,第二个想法似乎测试也会超时,因为黄瓜假设根据步骤定义函数中的参数数量需要回调。 不过,这可以通过优先考虑返回的承诺来解决。

@andyearnshaw感谢您的投入,我听到您对“仅用于 stepdef 中的箭头函数”的依赖项的担忧。 这个库基本上是我个人用来获得无状态步骤定义的解决方案,我只是为任何感兴趣的人打包了它。

将其视为围绕核心中这种纯 stepdef API 正在进行的辩论的临时解决方案(它已经持续了 4 年多,信不信由你)。 正如我所说,这是一个可以在某个时候进入核心的实验,我非常感谢实际使用它的人的反馈。 如果它得到足够的牵引力,那将成为整合黄瓜的更好论据。

另外,请注意它提供了一些其他(小)有用的功能工具: tap()和强制只读上下文。

stepdef 函数的 arity 检查绝对是我必须在这个库中规避的问题(以一种非常丑陋的方式)。 在 CLI 上和以编程方式将其关闭的选项对此非常有用(以及可能的其他用例)。 我很乐意这样做,但目前时间对我来说是一种稀缺资源。

随意从 Cucumber-fp 中获得灵感,同时修复 arity 检查。

@jbpro太好了,我真的很感激你在那里付出的努力。 我更不同意这个问题应该结束的观点。 我会看看你的图书馆,看看它是否能帮助我解决那个烦人的检查。 🙂

@andyearnshaw是的,感谢您的澄清。 我同意我们不应该放弃这个想法,并且保持这个问题的公开可能是保持事情透明的好方法,确实。

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