Mocha: 使用 ES6 的箭头函数时 this.timeout() 失败

创建于 2015-12-21  ·  59评论  ·  资料来源: mochajs/mocha

将 Node >= 4 与“use strict”和 ES6 语法用于箭头函数时,mocha 会失败:

describe('foo', () => {
  this.timeout(100);
});

# => TypeError: this.timeout is not a function

使用 ES5 语法确实有效:

describe('foo', function() {
  this.timeout(100);
});

那么,mocha 与this有哪些丑陋的技巧?

最有用的评论

谢谢。

为什么这么多“魔法”最终会产生问题? 为什么不是这个?:

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', () => ... );  
});

没有全局变量,没有有问题的魔法……但只有 JavaScript。

所有59条评论

它将函数绑定到测试上下文,这是使用箭头函数时无法完成的。 来自http://mochajs.org/

screen shot 2015-12-21 at 8 06 34 am

对于那个很抱歉!

谢谢。

为什么这么多“魔法”最终会产生问题? 为什么不是这个?:

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', () => ... );  
});

没有全局变量,没有有问题的魔法……但只有 JavaScript。

您提出的是一个重大的突破性变化,并且正在讨论 https://github.com/mochajs/mocha/issues/1969#issuecomment-160925915 重写可能会引入这些类型的语义:)

很高兴知道。 谢谢。

@ibc好吧,它必须是

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', (suite) => ... );  
});

但是这两个不同的套件参数是相同的类型吗? 可能不是,所以你有不同的变量名来反映不同的类型

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', (test) => ... );  
});

无论如何,不​​能在需要上下文的函数中使用箭头运算符。

我不希望打破 #1969 的 BDD UI——无论如何,马上就会——尽管我可以被说服。 我希望我们保留现有的 API,并引入一个包含 BDD UI 的单独包。 Mocha 将附带一个使用现有 API 的 BDD UI 版本,但随后我们可以使用 lambdas 发布新版本的 BDD UI 包——用户可以选择是否明确升级到该包。

也许describesuite包装器的替代 ES6 语法可以解决这个问题:

describe({ feature: 'create stuff' , do () {
    it('abc', () => {
    }); 
}})

这至少允许在套件级别进行绑定。

这事有进一步更新吗? 我有这个

mocha = require('mocha');

mocha.describe('test', (suite) => {

suite.timeout(500);

suite.it('test', (done)=>
          )
    }
)

并获得 TypeError:无法读取未定义的属性“超时”

@mroien这不是摩卡错误。 箭头语法不是function的 1:1 替换。 请仔细阅读它的局限性

有什么事吗? 我喜欢提议的解决方案,如果只是因为我喜欢箭头函数并且在不需要它时厌恶“这个”

由于 timeout 仅与done ,为什么不简单地将 timeout 函数附加到 done 函数。

it('makes sense', done => {
    done.timeout(100);
});

@nomilous这仍然不起作用。 我遇到了类似的问题。 对我的情况有效的是在it块内调用setTimeout 。 例如

it('description', done => {
     const func = () => {
          // assertions
     };
     setTimeout(func, 10000);
});

@nomilous同步或返回承诺的案例也可能有超时。

@andela-engmkwalusimbi 这不应该起作用。 正如@boneskull写道

@mroien这不是摩卡错误。 箭头语法不是函数的 1:1 替代。 请仔细阅读它的局限性

对于仍然对此感到疑惑的每个人,请确保您了解箭头函数的含义,然后回到这里继续阅读(有很多资源可以比我更好地解释这一点)。

在这种情况下,这个箭头函数起作用的唯一方法是,如果我们更改bdd API 以将context对象传递给每个 Runnable 的(钩子、测试)回调,而不是利用this 。 这不是一个主意,但这是一个重大变化的地震,所以永远不会发生。 取而代之的是:

it('should do something', function (done) {
  this.timeout(9000);
  // stuff
  done();
});

它看起来像这样:

it('should do something', (context, done) => {
  context.timeout(9000);
  done();
});

这会破坏现有的所有异步 Mocha 测试,无论它是否向后兼容:

it('should do something', function (context, done) {
  // context is unused, but 'done' is now the second parameter
  this.timeout(9000);
  done();
});

我们可以提供一个替代的bdd实现来做到这一点,但是——它不会是默认的。

这是我对“这个问题在哪里”的最彻底的解释。 :微笑:

也许可以在下一个主要版本中考虑它? 我不认为这是一个足够重要的变化来创建一个替代的 bdd 实现。 命名参数也可能有助于未来的开发,并且可能创建一种简单的方法来添加某种测试中间件,如下所示:

it('should do something', function ({ context, done }) { ...

我们可以提供一个替代的 bdd 实现,但是——它不会是默认的。

@boneskull一个新的 bdd-es6 界面会很棒:)

虽然我很喜欢箭头函数,它对诸如.filter(i => i.val)类的数组函数非常有用,但使用普通函数有什么问题? 我认为在全球范围内进行描述和它非常有用,因此我不必每次都需要它们。 另外从什么时候开始this魔术,只是因为您不了解(箭头)函数? 我绝对不想每次可以返回承诺时都提供变量,否则我很久以前就会切换到像 ava 这样的东西。 关于 mocha 的简单性,我认为 #1969 中描述的普通/箭头函数不应该有任何大的变化。 并且请不要告诉我箭头函数的输入速度更快,因为您的编辑器可以将单个f转换为function () {\n\t\n}

我不清楚,是否有解决方案可以超时使用箭头函数的before()调用?

    before( async function () {
      data = await provider.getData();
      console.log(data);
      this.timeout(30*1000);
    });

没有效果。 这里仍然有 4 秒超时。 这是我的测试套件中唯一缓慢的事情。 我可以在mocha.opts中将超时设置为 30 秒来解决该问题,但是我真的不需要所有测试都在 30 秒后超时,仅当 4 秒对 99% 的测试正常时一个 api 调用就可以了。

这是我同时解决它的方法(请注意,第一个describe()使用function而不是粗箭头语法:

describe('Search API', function () {
    this.timeout(30*1000);

    context('search', () => {
        let data;

        before( async () => {
          data = await provider.getSearch();
        });

        it('returns results', () => {
          expect(data).to.exist;
          expect(data.results).to.be.instanceOf(Array);
          expect(data.results.length).to.be.above(0);
        });
    })
});

@chovy您在await provider.getData()发生超时后设置超时。
尝试改用这个:

before(async function () {
  this.timeout(30*1000); // set timeout first, then run the function
  data = await provider.getData();
  console.log(data);
});

我不清楚,是否有解决方案可以使使用箭头函数的 before() 调用超时?

只是要明确这一点:目前无法使用箭头函数调用 Mocha 的timeout 。 任何关于替代方案(无论多么有价值)的讨论都是关于可能的新(或至少是修改过的)接口的讨论。

一段时间以来我一直在想的事情是能够做到:

it('...', (done) => {
  ...
  done()
})

it('...', (t) => {
  t.timeout(500)
  t.tag('integration', 'api')
  ...
  t.done()
})

使用相同的默认界面。

在相同的默认界面中同时支持这两种功能,让您可以开始在现有代码库中使用箭头函数。 值得指出的是,在许多在线教程中找到的(done)语法仍然可以工作,没有标志或任何东西。

因此,在此实现中,您将传统的done函数作为参数,但将实用函数添加为该done函数对象的属性。

使用this.timeout()会导致已用时间在报告中消失。

@dasilvacontin不知道为什么我们没有早点想到这一点。 这是个好主意。

@dasilvacontin哦,我记得。 因为你必须调用它。 你可能不想。

抱歉,@boneskull,您能否详细说明“必须调用它”? 你是说摩卡会认为测试是异步的问题吗?

在2017年1月29日,在05:54,克里斯托弗·希勒[email protected]写道:

@dasilvacontin哦,我记得。 因为你必须调用它。 你可能不想。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看,或将线程静音。

另外,您如何在使用“t”时声明进行异步测试的意图?

在磁带中,您总是必须调用“t.done”(在他们的 API 中为“t.end”),或设置断言的预期数量(“t.plan”)。

是否可以使用选项向 it() 添加第三个参数? 那不会破坏 API。

it ('accepts lambda', (done)=>{ doWork();  done(); }, {timeout:60000});

@骨头

如果不是把上下文放在第一位,而是一个可选的第二个怎么办?

取而代之的是:

it('should do something', function (done) {
  this.timeout(9000);
  // stuff
  done();
});

它看起来像这样:

it('should do something', (done, context) => {
  context.timeout(9000);
  done();
});
it('should do something', function (done, context) {
  // context is unused, but 'done' is now the second parameter
  this.timeout(9000);
  done();
});

我们可以提供一个替代的 bdd 实现,但是——它不会是默认的。

这是我对“这个问题在哪里”的最彻底的解释。 😄

或者如果上下文必须是一些定义的参数

it('should do something', function (done, override) {
  // context is unused, but 'done' is now the second parameter
  override.timeout(9000);
  done();
});

但我也会采用非默认界面:)

任何需要done解决方案的缺点是您必须使用done即使返回承诺会更简单。 就我自己而言,我知道我宁愿输入functionreturn不是.then(()=>{done()}, done)

@Flamenco这是一个有趣的想法,虽然我可能更喜欢最后使用该函数(是的,它在技术上更“神奇”,但是,让我们面对

@ScottFreeCode我绝对同意done我也不喜欢使用它,我只是在评论需要将上下文放在首位和最后,并使用上述由 bonekull 提供的示例。

我只是喜欢在上下文中传递或在 mocha 上设置一些开关以执行 es6 的选项。

同时,我只是将那些需要超时差异的测试描述包装在一个看起来很糟糕的 function() 调用中。

我找到了一个用例的解决方法,其中承诺从需要自定义超时的箭头函数返回到 mocha,我想分享,以防其他人发现它有用。

通过在像这样的测试声明中添加下面定义的when函数: it('does something', when(() => someFunctionReturningAPromise, customTimeout))可以在不放弃箭头函数的情况下设置 mocha 超时。

类似地,因为我们只使用 promises,我们可以将第一个参数重新带入测试并传入上下文而不是 done 回调: it('can access the mocha test context', when(testContext => someFunctionThatNeedsContext(testContext))

const when = (lambda, timeout) =>
  function() { // return a plain function for mocha to bind this to
    this.timeout(timeout || this.timeout() || 1000)
    return lambda(this)
  }

一些测试用例来说明:

const delay = timeout =>
  new Promise((resolve, reject) => setTimeout(resolve, timeout))

const deject = timeout => // similar to above, but a delayed reject
  new Promise((resolve, reject) => setTimeout(reject, timeout))

describe('mocha testing', () => {
  context('with only arrow functions', () => {
    context('tests that do not time out', () => {
      it('passes fast', when(() => delay(10), 100))
      it('does not usually time out', when(() => delay(2000), 2010))
    })
    context('tests that will time out', () => { // these should fail if the 'when' function works properly
      it('times out fast', when(() => delay(1000), 10)) // will fail in 10ms
      it('times out', when(() => delay(1000), 1000)) // will fail in about 1000ms
    })
    context('tests that will reject', () => { // this shows the when function works as expected when a test rejects
      it('fails fast', when(() => deject(10), 100))
    })
  })
})

@astitt-ripple 是的,或者你只是写function () {} ... Wtf?

好的卢卡,咬人。 :-)

与箭头函数的不同之处在于可以跳过返回。 在一个
es6 类型设置,这已经是 promise 'then' 的常见模式
链。

使用功能块,正如您所建议的,据我所知,返回
承诺必须是明确的。 最低限度用于基于承诺的测试功能
{... } 是错误的,测试必须总是返回它的承诺,所以最小的
有效的平凡化实际上是:function { return ... }。 否则
测试返回 undefined 给 mocha 而不是分派的承诺......
测试作者过得不好。

如果代码库中的大多数代码已经是 promise.then 中的箭头函数
和/或函数式编程风格,添加函数返回可以看起来
不一致。 建议的“何时”表格可供以下人员使用
比传统功能更喜欢箭头和更实用的风格
回调或函数返回样式。 它更简洁,适合
描述上下文它 dsl 我们都同意我们喜欢编写测试,考虑到
考虑 javascript 处理的承诺异步编程抽象
好。 即使没有我们的朋友卷曲,它也比函数+返回短:
当(() => ...)。

也许那不是这个项目的首选风格,我明白,而且
我不建议将其作为对项目的更改。 也许它甚至
正如你的wtf所暗示的那样应受谴责。 没关系。 摩卡必须与
pre-fp 友好的 js。 向后兼容性是首要问题。 那
也有道理。

这是一个漫长而死锁的线程,这是一种出路。 一个很好的
关于 javascript 的事情是不必有一种方式或一种风格
把事情做完。 我们完全不必就风格达成一致。 功能上
说起来,我之前的帖子给了人们一种使用箭头和承诺的方法
始终如一并使用测试 dsl,而不会放弃对 mocha 的访问
上下文,以一种干净和函数式编程友好的方式
之前有人建议过。

谢谢。

如果代码库中的大多数代码已经是……函数式编程风格……

...然后使用this.mutatingMethod(currentConfiguration)来设置行为,尤其是已经执行的函数(或者更确切地说是子例程)的行为,比必须编写return (这只是语法)更加不一致,并且不一致的外观会使现实更加明显,而不是实际引入不一致。

(不要误会我的意思,我对 JS 中函数式编程思想越来越流行感到非常满意;但是function / return vs =>的语法是实际上对于函数式编程并不是必不可少的,只是在该范式中看起来更清晰的问题,而其他概念,例如纯洁性和声明性与命令式实际上是必不可少的。拥有一个测试运行器会很整洁,相反,它具有更多功能它的行为/语义,此时切换到箭头函数可能是微不足道的......)

我同意你的观点, this.timeout 是突变的,并且打破了函数式编程范式。 当然,它完全不一致。 目前,除了 this.timeout 之外,还有其他方法可以声明每个测试的自定义超时吗?

在 mocha 的当前实现中,似乎有必要转回到命令式/突变范式,或者放弃设置每个测试超时。 非此即彼。

在上面的when抽象中,超时是 when 的第二个参数,允许功能样式保持在测试级别。 它隐藏了从 FP 模式回到命令式编程的不可避免的逃逸,但它在一个地方完成。 此外,它还允许箭头函数访问 mocha 上下文,否则在不破坏测试函数参数约定的情况下这是不可能的。 因此,在有人探索您的测试运行器想法之前,它可能会在此期间为该项目的某些用户(根据此问题的历史记录判断)解决一些问题。

也请不要误会我的意思。 我认为函数式编程永远不会或应该完全取代图灵机派生的命令式/突变式编程。 例如,在几乎所有情况下,在 JS 运行时代码,无论是否具有功能,最终都由以更传统的命令式风格(可能是 C++,但不一定)编写的程序解释,运行在也围绕突变编写的操作系统上和命令式的想法(可能是 C)。 内存是固定资源,不可变数据结构是运行时告诉我们的谎言。 这种基本的命令式模型是计算中的默认模型,并且将继续存在。 但这并不意味着函数式编程不能共存于它之上。 如果是这样,FP 代码不得不时不时地下降到底层模型是不可避免的。 我不认为这应该意味着我们举手并说出它的所有语法,让我们只使用函数/返回。

实际上,如果对细节有一定的容忍度,我们可以在 C 中进行函数式编程,就像您可以使用函数/返回而不是 => 函数进行函数式编程一样。 只是不要忘记回报你的承诺。 C 中的 FP 需要更多的输入,毕竟这只是语法....../s

归根结底,箭头函数让我们越来越接近一种可行且实用的方法,以一种流行的语言在 lambda 演算模型中工作。 删除那些额外的字符会产生一个很小但很重要的区别。 仍然存在许多实际限制,但其中许多是可以解决的,手头的主题就是其中之一。

无论如何。 我将使用我建议的辅助函数,其他人现在也可以免费使用它。 我期待着您的测试运行程序解决方案,但与此同时,我不知道继续尝试说服我们自己对哪些语法重要或不重要的意见是否会特别有成效,好像有是一种方式。 我喜欢箭头,而你喜欢函数。 我还没有看到改变我观点的有说服力的论点。 我对此持开放态度,但最好比下意识地更深思熟虑:“只需使用函数,wtf”或“您仍然可以使用更详细的_syntax_来执行 fp”。

用另一种方式解决问题,然后没有人需要使用when 。 纳夫说。 :-)

我不认为这应该意味着我们举手并说出它的所有语法,让我们只使用函数/返回。

我喜欢箭头,而你喜欢函数。

...最好比下意识地更深思熟虑,例如:...“您仍然可以使用更详细的语法进行 fp”。

不过,这与我所说的几乎相反 - 我想要这样一个事实,即仍然使用this箭头函数首先不会是 FP,它们将是带有箭头函数的 OO( FP 与传统 JS 函数的反函数)。 换句话说,它远非只是一种不同的语法,真正的问题在于,除了语法不兼容之外,还有更深层次的范式不兼容(因为 Mocha 目前是这样设计的)。

我相当确定有可能在 Mocha 之上构建一个替代界面,用参数完全替换this 。 我只想说清楚,如果你想在编写测试时使用 FP,你需要做的不仅仅是想出一种方法来将 Mocha 的this传递给箭头函数。 我非常上升到那种挑战的,但是。 ;^)

(还有其他一些 Mocha 行为是有状态的、全局的,或者——更糟——两者都有,但我现在没有时间想出一个简短的方法来列出它们。如果你见过不止一次运行 Mocha 的问题,不过,这就是其中的一个例子。)

目前,除了 this.timeout 之外,还有其他方法可以声明每个测试的自定义超时吗?

不幸的是,我很确定没有; 在我的脑海里,建议接受it的附加参数,这将是配置设置的键值映射(作为 JS 对象)听起来像是 Mocha 中一个不错的未来解决方案,如果有人愿意的话尝试实施它。

目前,除了 this.timeout 之外,还有其他方法可以声明每个测试的自定义超时吗?

不幸的是,我很确定没有;

感谢您确认此详细信息。 这巩固了我的观点。

在我的脑海中,接受一个附加参数的建议是配置设置的键值映射(作为 JS 对象),这听起来像是 Mocha 中一个不错的未来解决方案,如果有人想尝试实现它的话。

+1 以获得更通用的解决方案。

不过,据我所知,仍然传递单个参数似乎是可行的。 将thisdone回调结合起来(这样this就变成了一个函数)。 然后让 mocha 运行每个带有 promise 的测试(好吧,实际上有两个,一个用于处理超时,一个用于实际运行测试),而不管参数计数(与今天的工作方式不同)。 然后它可以检查函数的结果是否是一个承诺。 如果没有,则在同步函数返回后调用done完成测试。 如果函数的结果是一个 promise,则等待它解决(或拒绝)。 如果发生超时,则停止测试(与之前相同)。 在测试设法调用done并返回承诺的情况下。 在解析之前调用 done ,在这种情况下, mocha 应该等待承诺,然后由于完成序列不明确而使测试失败。 或者在解决之后的某个时间调用完成,在这种情况下,测试必须以某种方式追溯失败 - 或者问题以其他合理的方式发出信号。 对不起,这是很多粗略的笔触,但这是我的局外人对摩卡试图做的事情以及它遇到的怪癖的理解。 还有哪些其他考虑因素可能会阻止这是一个可行的解决方案?

如果你想在编写测试时使用 FP,你需要做的不仅仅是想出一种方法将 Mocha 的 this 传递给箭头函数

同意。 使用不同的计算模型肯定会发生变化,此外,javascript 是一个复杂的生态系统,需要考虑很多。 在我的特定用例中,但是不时设置超时(例如根据某些计算而不是固定的默认值更准确),是我使用 Mocha 编写 FP 测试时遇到的唯一实际问题(到目前为止至少)。 这很棒。 :+1:

也就是说,我想知道您认为还有哪些与 Mocha 相关的迫在眉睫的障碍(而不是在 FP 中编写测试通常意味着什么)。

不过这和我说的差不多

如果我描述错误或误解,我很抱歉。 对于我写的很多内容,根据回复,我不确定我是否按预期理解。 这是不幸的,因为我认为如果我们认真对待它,我们可能会非常接近 FP _应该_在_理论_中的样子。 然而,我们似乎不同意在今天当前可用的 Mocha 版本中对 _practice_ 进行可行的减少是什么样的,至少对于某些用户/用例。 所以我不太确定我提议的附加功能的主要问题到底是什么,从你这边来看。

(按顺序引用和回复,但可以说更重要的事情是晚而不是早。)


不过,据我所知,仍然传递单个参数似乎是可行的。 将它与 done 回调结合起来(所以这变成了一个函数)。 那么......还有哪些其他考虑因素可能会阻止这是一个可行的解决方案?

如果我们打破向后兼容性,我们可以切换到更简单的设计。

如果我们保持向后兼容性,这两个测试都需要通过而不是超时:

it("runs immediately", () => {
  // call functions and assert whatever
})
it("runs asynchronously", doneWithSomeOtherName => {
  setTimeout(() => {
    // call functions and assert whatever
    doneWithSomeOtherName()
  }, 100)
})

欢迎您尝试提出一些示例代码来证明并非如此(尽管我建议将重点放在此评论末尾的建议上),但我很确定没有任何设计能够做到这一点,并且让这个测试通过而不是超时:

it("looks just like an asynchronous test with a different name for `done`, but never tells Mocha it's done", context => {
  context.configureByMutation("some value")
  // call functions and assert whatever
})

但是,请注意那里的突变。 更多关于这个下面。


在我的特定用例中,但是不时设置超时(例如根据某些计算而不是固定的默认值更准确),是我使用 Mocha 编写 FP 测试时遇到的唯一实际问题(到目前为止至少)。 这很棒。

:+1:!


也就是说,我想知道您认为还有哪些与 Mocha 相关的迫在眉睫的障碍(而不是在 FP 中编写测试通常意味着什么)。

我想我可能没有非常专注地传达这一点... edly,所以让我看看我是否可以进一步简化它。 (如果其中任何一个被认为是对立的,也很抱歉;它当然不是故意的,尽管我承认在这里尝试改变思维方式。)Mocha 的代码库非常类似于类层次结构和 getter 和 setter 风格“面向对象”(Mocha 有多个问题或潜在问题,我认为归结为它的可变状态),但是如果您所做的只是编写测试并让 Mocha 运行它们,那么这通常不会影响您的测试代码. 你可以用 Mocha 的命令式变异配置做一些奇怪的事情:

it("imperatively sets the timeout multiple times", function(done) {
  this.timeout(5000)
  var context = this
  setTimeout(function() {
    context.timeout(1000)
    setTimeout(done, 500)
  }, 4000)
})

……但你没有必要。 与非函数式语言中函数式编程的许多元素一样:只是不要滥用命令式的东西。

(还有一个论点认为异常是不纯的,但我还不确定基于输入抛出的异常是否如此——这可以被认为是另一种形式的输出。所以有些人会说使用断言那个 throw 不起作用,但我现在不打算解决这个问题。)

(这里的重要部分:)我想说的是,我们正在考虑可能为已经复杂的代码库添加复杂性,或者进行向后不兼容的更改。 我们需要为这些事情中的任何一个辩护。 如果理由是“使测试更具功能性”,那很好(无论如何在我的书中)。 使测试更具功能性的设计可能值得麻烦(取决于它有多少麻烦)。 但是,如果通过“使测试更具功能性”您的意思是“使箭头函数改变事物”,即“使箭头函数的功能性降低”,则会看起来更实用(就像箭头函数看起来一样干净!)同时保留所涉及的一点点突变,无论这一点开始时有多小,几乎和实际一样令人信服摆脱那一点突变将是 - 至少如果重点是使测试更具功能性。

不过,我可能甚至不应该在这个切线上走得太远; 有关解决方案,请参见下文。 😸


所以我不太确定我提议的附加功能的主要问题到底是什么,从你这边来看。

(这里也是重要的部分:)实际上,我喜欢将timeout作为参数而不是方法调用的部分! 如果您能想出一种方法将其推广到 Mocha 的其余配置方法(有很多 - 如果我没记错的话,有些适用于同步测试,因此为什么我们不能只添加相同的方法作为done上的属性并让人们编写可以通过done调用配置的异步测试,但我离题了),那么我肯定想看看它。 至少,我们可能要使它成为一个建议,我们甚至可能能够通过执行到it当通过第三个参数(或类似的东西,也许it.configuredit(...).configured(...)如果我们不想要更多的参数数量恶作剧......) - 我认为这将是一个向后兼容的解决方案,可以解决潜在的突变/必要事项并获得箭头函数支持“正确的方式”(无论如何我正在争论的是这样):因为它适合新行为。 我想我应该说,而不是在解决方法中追求this ,而是:让我们扩展它的参数部分!

我发誓我在某个地方读到过你可以做这样的事情:

describe('a slow thing', () => {
 // test code...
}).timeout(5000);

这不会将参数契约更改为提供的函数。 现在我找不到任何此类事情的参考,所以也许我只是想象一下。

@thom-nic 这有效! 我想这是有道理的,因为所有 mocha 的函数都返回它的上下文

return this;

当用普通函数替换箭头函数形式时,它对我有用

功能() { ..... }

嗨伙计。 我很抱歉在 8 月引发讨论后昏昏欲睡,我一直很忙,实际上大部分时间都完成了/离开了那项工作。

我很欣赏关于不同用例的详细回应,以及如何将它们结合在一起。 这是我读过的 mocha 必须支持的不同设置的最简洁的展示。 感谢您抽出宝贵时间参与其中。

回想起来,很明显我一定是过分强调了访问 mocha 上下文( this ),而这方面实际上更像是一个方便的事后想法。 我没有意识到它会多么容易地将注意力从我实际尝试做的事情上转移开:通过在测试 dsl 中添加一个临时扩展 ( when ) 以简化一次性操作,这很有趣超时调整(加上消除特定测试风格的常见错误,我将在下面解释)。 返回this只是我想添加的另一件有趣的事情,主要思想(因此名称when )是用于处理需要与正常超时不同的情况。

显然,如果我想访问绑定上下文,我可以像许多人建议的那样直接使用function ,而不是用包装器将其提升。 那不是问题。 😄 我并不觉得这在表面上看起来很奇怪。 我希望,如果我展示了我是如何设置一些让我走上这条道路的测试开始的,它可能会完善图片。 需要明确的是,我不是要在这里销售任何特定的款式,使用适合您的款式。 这对我有用。

好的
首先,假设我们正在测试一些基本上只做一件事的设置,但会针对各种输入进行测试,因此我们必须在一系列场景中测试这个东西,以确保输出是正确的. 然而,由于相关的应用程序代码“做一件事”,底层的测试过程几乎总是相同的。 我也不想不必要地复制/改变测试主体,因为这会减慢我为新输入添加更多测试用例的速度,最终维护将变得不合理。

因此,我们编写了一个足够通用的函数,它可以使用任何可能支持的输入启动应用程序代码,执行测试操作,然后断言结果……在我的情况下,我正在使用 Promise 抽象(出于以下原因)我会省略),所以这个通用的测试过程函数自然必须返回那个 promise.then 链。 面包和黄油 es6 之类的东西,到目前为止还不错。

现在是测试场景,因为我们将所有内容都打包到我们的测试过程函数中,测试用例有效地定义了输入并调用了该函数。

所以也许我写了一堆这样的测试,一切_似乎_都在工作:

it('does something', function() {
  testProcedureFunction('something','1')
})

如果你密切关注,你可能已经注意到这个例子有一个错误。 它缺少它的return ,并且由于 testProcedureFunction 建立在承诺(双关语)之上,无论最后的断言是否通过或失败,它总是会通过。 这是一个错误,有时可能是一个非常微妙的错误。 为了说明,根据我们编写 testProcedureFunction 的方式以及编写应用程序的方式,假设开始时有一些同步代码,并且它会爆炸而不是测试结束断言,测试用例甚至可能失败——导致我们认为一切都很好。

测试当然应该看起来像这样并返回:

it('does something', function() {
  return testProcedureFunction('something','1')
})

现在我知道在大多数情况下这个测试将是一行。 实际上,每种情况都是一行,除非输入需要更大的超时时间。 现在,在经典 js 函数和箭头之间的差异中,箭头函数的一个特殊方面在这里很有用:当省略大括号时,单语句箭头函数具有隐含的返回值。 如果不是编写function {...} ,而是使用=> ... ,那么我可以轻松地扫描这些情况以查找箭头和缺少大括号,并迅速推断他们不能缺少这个return问题,并且需要一些额外的步骤(重新添加大括号)才能让某人搞砸。

像这样:

it('does something', () => testProcedureFunction('something','1'))

现在,如果这些情况之一比其他情况需要更长的时间怎么办! 我们当然可以像这样设置超时:

it('does something slow', function() {
  this.timeout(10000)
  return testProcedureFunction('somethingSlow','2')
})

或者也许有人会犯错误并先执行此操作(这当然不起作用):

it('does something slow', () => {
  this.timeout(10000)
  return testProcedureFunction('somethingSlow','2')
})

但是现在我们回到了开始的地方,代码库有一个成熟的模式可以重复,这很容易受到丢失返回错误的影响(未来的我,或者下一个添加功能的人——关键是,它很容易犯的错误,它可能会被忽视,并且很难追查)。 when函数解决了这个问题,让我们再次一致地使用箭头:

it('does something slow', when(() => testProcedureFunction('somethingSlow','2'), 10000))

(注意,我无法让上面的.timeout(5000)点链建议起作用,可能是由于我需要使用的 mocha 版本,我不记得了,会给一个尝试!)
(注意 2,注意when的使用不是使用this参数提升技巧——这真的只是事后的想法)。

也许有 linter 可以标记丢失的承诺返回错误(或者可能更现实的是,为每个函数强制执行带有 rhs 的 return 语句)。 然而,这在当时不是一个选择,而且我认为箭头语法更短,我发现它(主观/个人)更容易阅读和使用,这使我远离function .

所以你有它。

我不知道我是否有时间尽快再次回复,所以我希望这至少是信息丰富和清晰的,甚至可能将围绕整个“从箭头访问摩卡语境”内容的一些争议放到床。

最后,由于我发现 function vs => 混淆了很长时间,我将删除此链接,以防任何随便阅读为什么箭头无法访问this人都不清楚。 这是我发现的对函数与箭头的最清晰的解释,并且帮助我最终充分理解差异以完全自信地使用它们。

https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/

@thom-nic 它适用于it ,但不适用于describe

describe('my test suite', () => {

  it('test case 1', () => {
    // ...
  }).timeout('10s');  // Works on `it`. This test case doesn't timeout

  it('test case 2', () => {
    // ...
  });  // This test case timeouts.

}).timeout('10s');  // Doesn't work on `describe`. Tests are already created when runs to here.

@thom-nic ,你可以使用正常的函数形式

describe('my test suite', function() {
this.timeout(n);

...
}

任何抱怨这个的人都不理解箭头函数。

箭头函数不是一个新奇的 ES6 东西,它应该取代经典的function () {} 。 箭头函数的唯一目的是它从它的父级继承this ,其中经典的function ()有它自己的this

是的,即使使用完整的 ES6 语法,如果您想在函数的正确上下文中使用this ,您仍然应该使用function () 。 您应该在 ES6 应用程序中同时使用function ()() => ,具体取决于您要执行的操作。

this.timeout()不适用于it('....', () => { ... })因为回调从父describe()函数继承this ,其中this.timeout()不在那个层面上有意义。

难道 lambda 函数还允许您自动向函数发送单个参数,而无需在调用中声明它?

(param)=>aFunction
...然后(一个函数)

function(){} 可以绑定到“this”,但 ()=> 被“锁定”

当接收者在调用它的同一上下文中期望预先确定的“this”时,箭头函数应该取代传统函数(并且还可以从键入更少的代码中受益)。

我什至会说永远不要使用 function() 除非您希望“this”在调用它时不是“this”。

@弗拉门戈...

难道 lambda 函数还允许您自动向函数发送单个参数,而无需在调用中声明它?

我不确定我_完全理解_你是如何表达的。
就“向函数发送参数”而言,粗箭头的工作方式与常规函数一样,只有一个例外:如果您正好有 1 个参数,则可以省略括号。

() => console.log("hi"); //zero arguments requires empty parenthesis
a => console.log(a); //you can optionally leave the parenthesis off for 1 argument
(a,b) => console.log(`${a} ${b}`); //2..n arguments requires parenthesis

您可能已经了解到,只要您的函数是单个表达式,粗箭头就允许您通过省略花括号和return关键字来_返回_一个值。
所以如果你有...

setTimeout(function(a,b) { doSomething(); return calculateSomething(a,b); }, 5000);

...并且您想将其转换为粗箭头函数,您将无法动摇大括号和 return 关键字,因为函数体有多个语句。 你会这样做...

setTimeout((a,b) => { doSomething(); return calculateSomething(a,b); }, 5000);

如果,而是你开始...

setTimeout(function(a,b) { return calculateSomething(a,b); }, 5000);

...那么您正在处理一个如此简单的函数,它只返回一个表达式,您可以使用...

setTimeout((a,b) => calculateSomething(a,b), 5000);

这让阅读变得容易多了!
我在codefoster.com/levelup-arrays 上写了更多关于这个的

JavaScript 中有许多不同的编码风格——从 OOP 到 FP,从严格的类型安全到 mixin/duck-typing。 此外,每种风格都有高级模式(即 OOP 阵营中的依赖注入,FP 阵营中的 currying/monad)。

如果您的编码风格更接近 FP,其中不使用this并且使用箭头函数来减少样板,则必须保留this是任何高级测试的额外开销(例如参数化测试,创建DSL)。

任何有经验的开发人员都可以预先包装测试框架以适应他们的编码风格,但这意味着该框架不太“开箱即用”。 这转化为升级、采用插件和新工程师入职的额外工作。

我喜欢替代bdd接口的想法,它不使用this而是将通常是上下文对象的内容作为参数传递给describeit和钩子。

但实施起来并不那么简单,IIRC。 不过看到尝试会很酷。

我知道这会带来严重的副作用,但是您不能处理像这样的 done-or-context 参数吗?

it("runs immediately", () => {
  // call functions and assert whatever
})
it("runs asynchronously", doneOrContext => {
  setTimeout(() => {
    // call functions and assert whatever
    doneOrContext();
  }, 100)
})



md5-b1fe6f00c87a2916712cf6a4df16e142



it("runs immediately using the parameter as a context", doneOrContext => {
  doneOrContext.configureByMutation("some value");
  // As well as changing config, also flags to Mocha that this test is treating the
  // parameter as a context object and is therefore not async.
  // Call functions and assert whatever
})



md5-b1fe6f00c87a2916712cf6a4df16e142



it("runs asynchronously using the parameter as a context", doneOrContext => {
  doneOrContext.configureByMutation("some value");
  doneOrContext.setAsync(); // Flags to Mocha that even though the parameter has been used as
  // a context object, the test is in fact asynchronous.
  setTimeout(() => {
    // call functions and assert whatever
    doneOrContext();
    // or doneOrContext.done()
  }, 100)
})

我使用了下面的脚本,但我得到了相同的超时超过错误。

我的脚本:

描述(“getBillingDetail”,异步函数(){
this.timeout(55000);
it.only("检查给定的有效作业名",异步函数(完成){
this.timeout(55000);
var 结果 = 等待 url.getBillingDetail('12254785565647858');
控制台日志(结果);
assert.equal(result,true);
});
});

错误:超过 55000 毫秒的超时时间。 对于异步测试和钩子,确保调用“done()”; 如果返回 Promise,请确保它已解决。

不要将完成的回调传递给异步函数

我创建了一个向后兼容的原型解决方案。 现在它是一个单独的模块,但功能可以很容易地合并到mocha

https://github.com/papercuptech/mocha-lambda

快捷方式

require('mocha-lambda')
// now a global '_tst' can be used where 'this' was before

describe('suite', () => {
  beforeEach(() => {
    _tst.answer = 42
  })

  it('provides "this" as "_tst"', function() {
    assert(this === _tst)
  })

  it('works', () => {
    assert(_tst.answer === 42)
    _tst.skip()
  })
})

用于显式命名(并与 TypeScript 一起使用)

// if you were previously explicitly importing api (describe, it, etc.) from 'mocha',
// you will have to change to importing from 'mocha-lambda', until (or if) this
// gets merged into mocha proper
import ctx, {describe as d, it as i} from 'mocha-lambda'

d('suite', () => {
  // ctx() is a function that returns "this" as appropriate
  i('works using ctx()', () => {
    ctx().skip()
  })
})

import {Context} from 'mocha'
// ctx() can also rename global
ctx('t')
declare var t: Context
d('suite', () => {
  // ctx() is a function that returns "this" as appropriate
  i('works using renamed global', () => {
    t.skip()
  })
})

@papercuptech 找不到链接 404。

Woops .. 是私人回购。 现在公开

也可以 npm i mocha-lambda

@aleung @linesh-simplicity,这被#3485 取代

谢谢。

为什么这么多“魔法”最终会产生问题? 为什么不是这个?:

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', () => ... );  
});

没有全局变量,没有有问题的魔法……但只有 JavaScript。

看到@thom-nic 的回答,干净并且成功了

我发誓我在某个地方读到过你可以做这样的事情:

describe('a slow thing', () => {
 // test code...
}).timeout(5000);

这不会将参数契约更改为提供的函数。 现在我找不到任何此类事情的参考,所以也许我只是想象一下。

@thom-nic 解决方案对我有用,谢谢!

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

相关问题

CADBOT picture CADBOT  ·  3评论

luoxi001713 picture luoxi001713  ·  3评论

eschwartz picture eschwartz  ·  3评论

3p3r picture 3p3r  ·  3评论

jamietre picture jamietre  ·  3评论