我是javascript单元测试的新手,但我相信至少对单元测试有一点认识。
我正在尝试学习“javascript方式”#39;并开始为我的客户端代码编写单元测试。
为此,我安装了以下库: mocha.js和chai.js
在一些令人惊讶的投票率中,情况一切顺利。 我设法轻松地包含这些东西并开始编写我的单元测试。
我在ASP Core btw工作。虽然我发现VS2016是我曾经使用过的这个IDE的最错误的版本(但我已经专业5年了,所以我可能错了)使用新的MVC 6更改它提供了一种简单的方法来管理您的静态资源(允许您使用bower包管理器。或者客户端依赖项。
所以,回到测试阶段。我从一小部分通用和简单的javascript函数开始,我已经开始了。虽然写得不好,但他们提供了很好的锻炼。
然而,过了一段时间我需要开始嘲笑。通过在C#中编写单元测试,我开始意识到这种类型库的有用性。 由于这些概念并非特定于C#,而是一般的单元测试,我期望找到一个能提供这种功能的库 - sinon.js。
所以,一切都进展顺利。
直到,我不得不嘲笑一个承诺。
再次证明,有一个库可以帮助我解决这个问题 - sinon-as-promised.js
经过一段时间的努力,通过npm安装它,使用像browserify这样的工具把它解析成一个在浏览器中使用的单个脚本,我相信我设法让它运行起来。
此时我可以做类似的事情:
let returnsPromise = sinon.stub($, "ajax").resolves('success');
这将使我成为一个"然后能够" object(具有then()方法并且应该表现为promise的东西)。 这里有一个小问题,它没有.done()和.fail()方法,我在编码时更喜欢这些方法,但我猜测有一种简单的方法可以将它们扩展到对象中。
在那之后,当我读到chai.js提供一种管理承诺表达的有效方式时,我甚至开始感到印象深刻。
在我的问题上,我们就是这样。
我正在谈论的关于柴的事情是"最终"支持。这个词让我做了魔术,并且无需在then()的成功和错误回调中使用.done(),只是为了确保我们没有在promise中得到误报。
所以,它应该像这样使用:
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)
我认为 - 我们不知道你的意思&#34;最终&#34;。这让我相信我添加chai-as-promised.js的方式不起作用。
在此之后,我尝试了很多东西。有些愚蠢,有些甚至更多,但无论我尝试过什么都不能让代码理解&#34;最终&#34;
这些事情包括:
由于我没有想法,我希望你们中的一些人尝试在浏览器中进行一些javascript单元测试。
因为它涉及相当多的资源,解释和内容,并且这篇文章已经比它应该的时间更长了,我创建了一个github存储库,我已经上传了迄今为止所做的一切。
这是一个指向它的链接: js-unit-test-mocking
顺便说一下,如果你没有使用ASP或任何与微软相关的东西,那么运行它所需的一切都应该在wwwroot中。
感谢您对我的承诺。对不起,很长的帖子。
答案 0 :(得分:2)
您的代码存在两个问题:
您正在使用expect
和should
个界面(expect(...).should.eventually.equal...)
。您应该同时使用其中一个,但不能同时使用两个。
您忘记使用chai.use
的导出值拨打chai-as-promised
。我推断这个事实,如果我故意省略这个,那么我得到完全相同的错误,我不知道有任何其他方法来获得该错误。
无论您想做什么,都转换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
(这就是{ {1}}选项确实)。输出位于-s
,这是您需要在浏览器中加载的内容。
然后您可以使用此chai-as-promise-built.js
文件:
index.html
这里的策略是使用browserify将所有内容编译成单个文件。对于某些项目,这是一个可行的选择。对于希望使用<!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>
编写代码而不是依赖于全局变量的项目,它也可能更受欢迎。这是一个说明性的测试文件:
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);
重建。
答案 1 :(得分:1)
我的意思是,你可以通过使用返回jquery延迟的函数来存储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();
你也可以创建一个帮助你的帮助器,以及一个总是用原始函数替换ajax的“afterEach”。
上述代码取决于https://github.com/domenic/sinon-chai。如果你不想依赖sinon-chai那么你可以只看一个间谍的“callCount”。像这样:
expect(callback.callCount > 0).to.be.ok;
答案 2 :(得分:0)
以下是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');