我是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。
谢谢你的耐心。不好意思,写了这么久
你的代码有两个问题:
-
您同时使用
expect
和should
接口(expect(...).should.eventually.equal...)
。 -
您忘记使用
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>
您将看到第一个测试将失败,因为它试图同时使用expect
和should
。后续的两个测试将通过。
如果你注释掉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;
以下是chaiAsPromised
与expect
的测试模式:
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);
});
在你的测试代码中,你混合了expect
和should
。
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');