配置javascript单元测试和承诺断言



我是javascript单元测试的新手,但我相信至少对单元测试有一点了解。

我正在尝试学习"javascript方式",并开始为我的客户端代码编写单元测试。

为此,我安装了以下库:Mocha.js和chai.js

在一些令人惊讶的投票率中,一切都进行得相当顺利。我设法轻松地包含了这些东西,并开始编写我的单元测试。

我在ASP Core工作顺便说一句。虽然我发现VS2016是我曾经使用过的这个IDE的最错误的版本(但我已经是一个专业的只有5年,所以我可能是错的)与新的MVC 6的变化,它提供了一个简单的方法来管理你的静态资源(或客户端依赖)通过让你使用一个更低的包管理器。

回到测试。我从一个小的通用和简单的javascript函数列表开始。虽然写得有点差,但它们提供了一个很好的练习。

然而,过了一会儿,我需要开始嘲笑。从用c#编写单元测试开始,我开始欣赏这种类型库的有用性。因为这些概念不是c#特有的,而是一般的单元测试,所以我希望找到一个提供这种功能的库——sinon.js。

所以,一切都进行得很顺利。

直到,我不得不嘲笑一个承诺。

再一次,我发现有一个库可以帮助我解决这个问题——sinon-as-promise .js

经过相当多的努力,包括通过npm安装它,使用像browserify这样的工具将它解析成一个在浏览器中使用的脚本等,我相信我设法让它运行起来了。

现在我可以这样做了:

let returnsPromise = sinon.stub($, "ajax").resolves('success');

,这将使我成为一个"then-able"对象(具有then()方法并且应该表现为承诺的对象)。这里有一个小问题,它没有。done()和。fail()方法,我已经知道在我的编码中更喜欢这些方法,但我猜有一种简单的方法可以将它们扩展到对象中。

在那之后,当我读到chai.js提供了一种管理承诺期望的有效方法时,我甚至开始印象深刻。

这就是我的问题。

我说的是chai的"最终"支持。这个词可以让我发挥魔力,并防止在then()的成功和错误回调中使用.done(),只是为了确保我们在承诺中没有得到假阳性。

所以,它应该这样使用:

it('testing eventually', function () {
    let returnsPromise = sinon.stub($, "ajax").resolves('success');
    return expect(returnsPromise()).should.eventually.equal('success');        
});

,如果没有得到想要的结果,那么这个操作就会失败。

现在,你可以想象当我不能像承诺的那样跑步时我有多失望。

我找不到这个库的"浏览器友好"版本,所以,在用npm安装它之后,再次将它浏览器化为"应该在浏览器中工作的脚本",我把它添加到聚会中。

然而,无论我做什么,在上面的例子中,我得到以下错误:
TypeError: Cannot read property 'equal' of undefined
at Context.<anonymous> (js/miscellaneous-tests.js:94:58)

我解释为——我们不知道你说的"最终"是什么意思。这让我相信我添加chai-as-promise .js的方式不起作用。

在这之后,我尝试了很多事情。有些愚蠢,有些甚至更愚蠢,但无论我怎么尝试,它都不能让代码理解"最终"

这些内容包括:

  • 读取脚本并获取chai-as-promise的依赖项并将其添加到解决方案中(包括browserify-ed和来自npm的形式),在第二个场景中对它们的依赖项再次执行相同的操作-直到我认为没有丢失包为止。
  • 我尝试添加,删除文件在这么多不同的方式,我实际上画了一片空白,而写这-但每一个可能的组合,我能想到-我试过。
  • 我搜索了"最终是未定义的"和类似的东西很多次,我觉得谷歌有点生我的气,但我找不到我要找的东西
  • 在某个时候,我开始在地板上画一个大圆圈,里面有一个五角星,但这花了我一段时间(像处女的血和蜡烛这样的东西现在在办公室里不那么容易找到),当我第二天回到办公室时,清洁女工已经把它擦掉了。

由于我没有想法,我希望你们中的一些人已经尝试过在浏览器中做一些javascript单元测试。

因为它涉及到相当多的资源,解释和东西,这篇文章已经超出了它应该的长度,我创建了一个github存储库,我上传了我到目前为止所做的一切。

是它的链接:js-unit-test-mocking

顺便说一句,如果你不进入ASP或任何微软相关的,一切需要运行它应该在wwwroot。

谢谢你的耐心。不好意思,写了这么久

你的代码有两个问题:

  1. 您同时使用expectshould接口(expect(...).should.eventually.equal...)

  2. 您忘记使用chai-as-promised的输出值调用chai.use。我是从这样一个事实推断出来的,如果我故意省略这个,那么我得到的错误就和你得到的完全一样,而且我不知道还有什么其他方法可以得到那个错误。

无论你想做什么,你转换chai-as-promised在浏览器中工作。既然你提到使用了browserify,我将在答案中使用它。您可以使用webpack,甚至编写自己的脚本来进行转换。

遵循两个一般策略。

只变换chai-as-promised

这里的策略是保持所有内容分开,并且只转换chai-as-promised以在浏览器中工作。下面是一个说明性的测试文件:

const expect = chai.expect;
mocha.setup("bdd");
chai.should();
// Commenting out this line reproduces the exact error message
// reported in the question.
chai.use(chaiAsPromised);
// Incorrect use of should with expect.
it('testing eventually incorrectly', function () {
    return expect(Promise.resolve("success")).should.eventually.equal('success');
});
// Correct use of expect alone.
it('testing eventually with expect', function () {
    return expect(Promise.resolve("success")).eventually.equal('success');
});
// Correct use of should alone.
it('testing eventually with should', function () {
    return Promise.resolve("success").should.eventually.equal('success');
});

chai-as-promised使用以下命令进行转换:

browserify -o chai-as-promised-built.js -s chaiAsPromised node_modules/chai-as-promised/lib/chai-as-promised.js

这实际上是将chai-as-promised.js封装到代码中,以便它在CommonJS方言中正常导出的内容在浏览器中成为全局导出,并且该全局导出被命名为chaiAsPromised(这就是-s选项所做的)。输出在chai-as-promise-built.js中,这是您需要在浏览器中加载的内容。

那么你可以使用index.html文件:

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/xhtml; charset=utf-8"/>
    <link href="./node_modules/mocha/mocha.css" type="text/css" media="screen" rel="stylesheet" />
    <script type="text/javascript" src="./node_modules/mocha/mocha.js"></script>
    <script type="text/javascript" src="./node_modules/chai/chai.js"></script>
    <script type="text/javascript" src="./chai-as-promised-built.js"></script>
    <script type="text/javascript" src="./test.js"></script>
  </head>
  <body>
    <div id="mocha"></div>
    <script>
      mocha.run();
    </script>
  </body>
</html>

Bundle into One File

这里的策略是使用browserify将所有内容编译到一个文件中。对于某些项目来说,这是一个可行的选择。对于那些希望使用require而不是依赖全局变量来编写代码的项目来说,它也可能是更可取的。下面是一个说明性的测试文件:
const chai = require("chai");
const chaiAsPromised = require("chai-as-promised");
const expect = chai.expect;
const mocha = require("mocha");
mocha.mocha.setup("bdd");
chai.should();
// Commenting out this line reproduces the exact error message
// reported in the question.
chai.use(chaiAsPromised);
// Incorrect use of should with expect.
it('testing eventually incorrectly', function () {
    return expect(Promise.resolve("success")).should.eventually.equal('success');
});
// Correct use of expect alone.
it('testing eventually with expect', function () {
    return expect(Promise.resolve("success")).eventually.equal('success');
});
// Correct use of should alone.
it('testing eventually with should', function () {
    return Promise.resolve("success").should.eventually.equal('success');
});

安装所需模块后,使用:

browserify -o built.js test.js

然后你可以加载这个index.html文件:

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/xhtml; charset=utf-8"/>
    <link href="./node_modules/mocha/mocha.css" type="text/css" media="screen" rel="stylesheet" />
    <script type="text/javascript" src="./built.js"></script>
  </head>
  <body>
    <div id="mocha"></div>
    <script>
      mocha.run();
    </script>
  </body>

您将看到第一个测试将失败,因为它试图同时使用expectshould。后续的两个测试将通过。

如果你注释掉chai.use(chaiAsPromised);行,你会得到与你在问题中报告的完全相同的错误信息。如果您确实更改了代码,请记住使用browserify重新构建。

我的意思是,您可以通过使用返回jquery延迟的函数来stub ajax来使这种方法更简单。可以使用如下命令:

var dfd = $.Deferred();
var callback = sinon.spy();
sinon.stub($, "ajax", function() {
   return dfd.promise();
})
$.ajax('...').done(callback);
dfd.resolve();
expect(callback).should.have.been.called();

您也可以创建一个帮助器来为您完成这些工作,以及一个"afterEach",它总是用原始函数替换ajax。

以上代码依赖于https://github.com/domenic/sinon-chai。如果你不想依赖sinon-chai,那么你可以看看间谍的"callCount"。这样的:

expect(callback.callCount > 0).to.be.ok;

以下是chaiAsPromisedexpect的测试模式:

var chai = require("chai");
var expect = chai.expect;
var chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
it("promise should return blah", function() {
  var blah = "blah";
  var result = somethingToTest();
  return expect(result).to.eventually.equal(blah);
});

在你的测试代码中,你混合了expectshould

return expect(returnsPromise()).should.eventually.equal('success');

编辑:来自Chai API文档:Chai Assertions for Promises

return doSomethingAsync().should.eventually.equal("foo");
遵循相同的模式,你的代码应该是:
return returnsPromise().should.eventually.equal('success');

最新更新