测试NodeJS中的承诺

时间:2015-10-06 18:22:10

标签: javascript node.js unit-testing promise

我正在尝试学习在NodeJS中测试承诺,而我在其他语言中使用的测试方法在这里让我失望了。基本问题是"如何在一个或多个链接然后(和完成或捕获)承诺块中有效地测试间接输入和输出?"

这里是lib/test.js的来源:

var Bluebird = require("bluebird"),
    fs = Bluebird.promisifyAll(require("fs"));

function read(file) {
    return fs.readFileAsync(file)
        .then(JSON.parse)
        .done(function () {
            console.log("Read " + file);
        });
}

function main() {
    read("test.json");
}

if (require.main === module) {
    main();
}

module.exports = read;

这里是tests/test.js

的来源
var Bluebird = require("bluebird"),
    chai = require("chai"),
    expect = chai.expect,
    sinon = require("sinon"),
    sandbox = sinon.sandbox.create(),
    proxyquire = require("proxyquire");

chai.use(require("chai-as-promised"));
chai.use(require("sinon-chai"));

describe("test", function () {
    var stub, test;

    beforeEach(function () {
        stub = {
            fs: {
                readFile: sandbox.stub()
            }
        }
        test = proxyquire("../lib/test", stub);
    });

    afterEach(function () {
        sandbox.verifyAndRestore();
    });

    it("reads the file", function () {
        test("test.json");
        expect(stub.fs.readFile).to.have.been.calledWith("test.json");
    });
    it("parses the file as JSON", function () {
        stub.fs.readFileAsync = sandbox.stub().returns(Bluebird.resolve("foo"));
        sandbox.stub(JSON, "parse");
        test("test.json");
        expect(JSON.parse).to.have.been.calledWith("foo");
    });
    it("logs which file was read", function () {
        stub.fs.readFileAsync = sandbox.stub();
        sandbox.stub(JSON, "parse");
        test("bar");
        expect(console.log).to.have.been.calledWith("Read bar")
    });
});

我意识到这些例子是微不足道的,但是我希望尝试理解如何测试承诺链,而不是如何读取文件并将其解析为JSON。 :)

此外,我并没有依赖于任何框架或类似的东西,所以如果我在抓取任何包含的NodeJS库时无意中选择了不好的话,那么也会感激呼出。

谢谢!

2 个答案:

答案 0 :(得分:2)

假设语法是Mocha,您需要返回承诺。当你从方法中返回它们之后,所有的promises都按返回值工作,所以如果你不从测试中返回它们,那么测试库就无法挂钩它们。

describe("testing promises with mocha", () => {
   it("works by returning promises", () => {
      return Promise.resolve("This test passes");
   });
   it("fails by returning promises", () => {
      return Promise.reject(Error("This test fails"));
   });
   it("Lets you chain promises", () => {
      return fs.readFileAsync("test.json").then(JSON.parse);
   });
});

(新功能箭头语法适用于NodeJS,如果您需要支持旧节点 - 将其转换为function(){个调用)

答案 1 :(得分:1)

@Benjamin Gruenbaum

我最终在lib/test.js中使用了以下内容:

var Bluebird = require("bluebird"),
    fs = Bluebird.promisifyAll(require("fs"));

function read(files) {
    return Bluebird.map(files, function (file) {
        return fs.readFileAsync(file)
            .then(JSON.parse)
            .then(function (data) {
                console.log("Read " + file);
            });
    });
}

function main() {
    read(["test.json", "test2.json"]);
}

if (require.main === module) {
    main();
}

module.exports = read;

这是tests/test.js

var Bluebird = require("bluebird"),
    chai = require("chai"),
    chaiAsPromised = require("chai-as-promised"),
    expect = chai.expect,
    sinon = require("sinon"),
    sandbox = sinon.sandbox.create(),
    proxyquire = require("proxyquire");

chai.use(chaiAsPromised);
chai.use(require("sinon-chai"));

describe("test", function () {
    var stub, test;

    beforeEach(function () {
        stub = {
            fs: {
                readFile: sandbox.stub()
            }
        };
        sandbox.stub(JSON, "parse");
        test = proxyquire("../lib/test", stub);
        stub.fs.readFileAsync = sandbox.stub().returns(Bluebird.resolve());
    });

    afterEach(function () {
        sandbox.verifyAndRestore();
    });

    it("reads the files", function () {
        var files = ["test.json", "test2.json"];
        return test(files).then(function () {
            var expects = files.map(function (file) {
                return expect(stub.fs.readFileAsync).to.have.been.calledWith(file);
            });
            // expects.push(expect(Bluebird.resolve(1)).to.eventually.equal(2));
            return Bluebird.all(expects);
        });
    });
    it("parses the files as JSON", function () {
        var returns = ["foo", "bar"];
        returns.forEach(function (value, index) {
            stub.fs.readFileAsync.onCall(index).returns(Bluebird.resolve(value));
        });
        return test(["baz", "buz"]).then(function () {
            var expects = returns.map(function (value) {
                return expect(JSON.parse).to.have.been.calledWith(value);
            })
            // expects.push(expect(Bluebird.resolve(1)).to.eventually.equal(2));
            return Bluebird.all(expects);
        });
    });
    it("logs which files were read", function () {
        var files = ["bleep", "blorp"];
        sandbox.spy(console, "log");
        return test(files).then(function () {
            var expects = files.map(function (file) {
                return expect(console.log).to.have.been.calledWith("Read " + file);
            });
            // expects.push(expect(Bluebird.resolve(1)).to.eventually.equal(2));
            return Bluebird.all(expects);
        });
    });
});

我在那里留下了注释掉的1 === 2个断言以确保我没有得到误报(比如.then因为我做错了而无法调用Employee.objects.all().prefetch_related('group_set')