mocha/chai.js 测试工具:https 调用在 after() 函数中不起作用

mocha/chai.js testing harness: https calls do not work from after() function

mocha.js:  3.4.2
chai.js:   4.1.0
node.js:   6.10.3
npm:       5.3.0

我已经为部署在 node.js 下的 RESTful 服务设置了一个简单的测试工具。测试工具依赖于 mocha.js / chai.js。每个通过 https 调用端点的测试都有效。同样,before()after() 函数被调用。但是:似乎不可能在 after() 函数中调用任何 RESTful 端点。为什么?

由于 after() 在测试结束时出现,我认为 RESTful 端点的异步调用根本没有时间 return 在测试过程退出之前。所以,我编写了一个忙等待循环来给响应一些时间。不行:端点调用在 after().

中根本不起作用

这是我的 package.json:

{
    "name": "so-mcv-example",
    "version": "1.0.0",
    "description": "Minimum, Complete, and Verifiable Example",
    "main": "index.js",
    "dependencies": {
        "restify": "^4.3.0"
    },
    "engines": {
        "node": "6.10.3",
        "npm": "5.3.0"
    },
    "devDependencies": {
        "chai": "^4.1.0",
        "chai-http": "^3.0.0",
        "mocha": "^3.4.2"
    },
    "scripts": {
        "test": "mocha"
    }
}

我的 index.js 看起来像这样:

const restify = require('restify'),
           fs = require('fs');

const server = restify.createServer({
    certificate: fs.readFileSync('security/server.crt'),
    key: fs.readFileSync('security/server.key'),
    name: 'so-mcv-example'
});

server.listen(443, function() {
  console.log('%s listening at %s', server.name, server.url);
});

server.pre(restify.pre.userAgentConnection());

server.get('/status', function(req, res, next) {
    res.send({ status: 'so-mcv-example is running ok.' });
    return next();
});

test目录下,我有mocha.opts:

--require ./test/test.bootstrap

test.bootstrap.js

process.env.NODE_ENV = 'test';

// CAUTION: never do this in production! For testing purposes, we ignore
// faults in the standard SSL handshaking.
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

后面的设置协商 index.js 中使用的自签名 SSL 证书。在 test/status-test.js 中,我有:

const chai = require('chai');
const expect = chai.expect;
const should = chai.should();
const server = 'https://localhost';

chai.use(require('chai-http'));

before(function() {
    if (process.env.NODE_ENV != "test") {
        this.skip();
    }

    // XXX Wipe the database.
    console.log("Wipe the database BEFORE tests.");
});

describe('GET /status', function() {
    it('passes, as expected', function(done) {
        chai.request(server)
        .get('/status')
        .end(function(err, res) {
            expect(err).to.be.null;
            expect(res).to.have.status(200);
            done();
        });
    });
});

after(function() {
    if (process.env.NODE_ENV != "test") {
        this.skip();
    }

    chai.request(server)
    .get('/status')
    .end(function(err, res) {
        expect(err).to.be.null;
        expect(res).to.have.status(200);
        console.log("2nd call to /status returned.");
        done();
    });
});

诚然,使用 chai 调用 after() 函数中的端点是人为设计的。那不是重点。关键是 console.log() 指令根本不会执行。即当我运行npm test时,结果为:

> so-mcv-example@1.0.0 test /Users/charlie/workspace/so-mcv-mocha
> mocha



Wipe the database BEFORE tests.
  GET /status
    ✓ passes, as expected (44ms)


  1 passing (56ms)

请参阅下面我的回答,了解为什么 console.log() 没有被执行。

为了解决这个问题,我添加了选项 --no-exitmocha.opts:

--require ./test/test.bootstrap
--no-exit

然后当我运行npm test时,输出想要的结果:

> so-mcv-example@1.0.0 test /Users/charlie/workspace/so-mcv-mocha
> mocha



Wipe the database BEFORE tests.
  GET /status
    ✓ passes, as expected (43ms)


  1 passing (55ms)

2nd call to /status returned.

我相信我已经找到了解决办法。在 after() 中,如果您在测试环境外部的服务上异步调用某个端点,则该异步调用会排队,不会立即执行。这就是node.js中调用异步函数的本质。好吧,如果在 after() 调用之后没有逻辑,那么 mocha 会立即通过调用 process.exit() 退出!最终结果是您对外部端点的异步调用保持排队状态并且从未被调用。

解决方案:在 test 目录下的 mocha.opts 中,只需安装选项 --no-exit.