Javascript 用 mocha 测试异步函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12159846/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 07:15:59  来源:igfitidea点击:

Testing asynchronous function with mocha

javascriptnode.jstestingtddmocha

提问by MadFool

I want to test a asynchronous javascript function that runs in node.js and makes a simple request to a http api:

我想测试一个在 node.js 中运行的异步 javascript 函数并向 http api 发出一个简单的请求:

const HOST = 'localhost';
const PORT = 80;

http = require('http');

var options = {
    host: HOST,
    port: PORT,
    path: '/api/getUser/?userCookieId=26cf7a34c0b91335fbb701f35d118c4c32566bce',
    method: 'GET'
};
doRequest(options, myCallback);

function doRequest(options, callback) {

    var protocol = options.port == 443 ? https : http;
    var req = protocol.request(options, function(res) {

        var output = '';
        res.setEncoding('utf8');

        res.on('data', function(chunk) {
            console.log(chunk);
            output += chunk;
        });

        res.on('error', function(err) {
            throw err;
        });

        res.on('end', function() {
            var dataRes = JSON.parse(output);
            if(res.statusCode != 200) {
                throw new Error('error: ' + res.statusCode);
            } else {
                try {
                    callback(dataRes);                        
                } catch(err) {
                    throw err;
                }
            }
        });

    });

    req.on('error', function(err) {
        throw err;
    });

    req.end();

}

function myCallback(dataRes) {
    console.log(dataRes);
}

Executed this code works and the response will be displayed as expected.

执行此代码有效,响应将按预期显示。

If I execute this in a mocha test the request is not executed:

如果我在 mocha 测试中执行此请求,则不会执行该请求:

describe('api', function() {
    it('should load a user', function() {
        assert.doesNotThrow(function() {
            doRequest(options, myCallback, function(err) {
                if (err) throw err;
                done();
            });
        });
        assert.equal(res, '{Object ... }');
    });
});

The Problem is, that no code after:

问题是,之后没有代码:

var req = protocol.request(options, function(res) {

is executed not even a simple console.log.

即使是简单的 console.log 也不会执行。

Can anybody help?

有人可以帮忙吗?

回答by Risadinha

You have to specify the callback doneas the argument to the function which is provided to mocha - in this case the it()function. Like so:

您必须将回调指定done为提供给 mocha 的函数的参数 - 在本例中为it()函数。像这样:

describe('api', function() {
    it('should load a user', function(done) { // added "done" as parameter
        assert.doesNotThrow(function() {
            doRequest(options, function(res) {
                assert.equal(res, '{Object ... }'); // will not fail assert.doesNotThrow
                done(); // call "done()" the parameter
            }, function(err) {
                if (err) throw err; // will fail the assert.doesNotThrow
                done(); // call "done()" the parameter
            });
        });
    });
});

Also, the signature of doRequest(options, callback)specifies two arguments though when you call it in the test you provide three.

此外,签名doRequest(options, callback)指定了两个参数,但当您在测试中调用它时,您提供了三个参数。

Mocha probably couldn't find the method doRequest(arg1,arg2,arg3).

摩卡可能找不到方法doRequest(arg1,arg2,arg3)

Did it not provide some error output? Maybe you can change the mocha options to get more information.

它没有提供一些错误输出吗?也许您可以更改 mocha 选项以获取更多信息。

EDIT :

编辑 :

andho is right, the second assert would be called in parallel to assert.doesNotThrowwhile it should only be called in the success callback.

andho 是对的,第二个断言将被并行调用,assert.doesNotThrow而它应该只在成功回调中调用。

I have fixed the example code.

我已经修复了示例代码。

EDIT 2:

编辑2:

Or, to simplify the error handling (see Dan M.'s comment):

或者,为了简化错误处理(参见 Dan M. 的评论):

describe('api', function() {
    it('should load a user', function(done) { // added "done" as parameter
        assert.doesNotThrow(function() {
            doRequest(options, function(res) {
                assert.equal(res, '{Object ... }'); // will not fail assert.doesNotThrow
                done(); // call "done()" the parameter
            }, done);
        });
    });
});

回答by Thomas W

If you have an asynchronous function that does not support callbacks, or if you think using unnecessary callbacks is... unnecessary, then you can also just turn the test into an async test.

如果您有一个不支持回调的异步函数,或者如果您认为使用不必要的回调是......不必要的,那么您也可以将测试变成异步测试。

instead of:

代替:

it('should be able to do something', function () {});

simply do:

简单地做:

it('should be able to do something', async function () {});
                                     ^^^^^

Now you can awaitasync functions:

现在您可以await异步函数:

it('should be able to do something', async function () {
  this.timeout(40000);

  var result = await someComplexFunction();

  assert.isBelow(result, 3);
});

回答by Antonio Ganci

I've done a very similar test in my project for an http client. I paste the code here and hope is useful. Here is the client (my nodejs server use express and I use promise for error handling):

我在我的项目中为 http 客户端做了一个非常相似的测试。我把代码贴在这里,希望有用。这是客户端(我的 nodejs 服务器使用 express,我使用 promise 进行错误处理):

var http = require('http');
var querystring = require('querystring');

module.exports = {
  get: function(action, params, res, callback) {
    doPromiseRequest(action, querystring.stringify(params), callback, 'GET', 'application/json')
      .then((response) => callback(response))
      .catch((error) => {
        res.status(500);
        res.render('error', {layout: false, message: error.message, code: 500});
      });
  },
}

function doPromiseRequest(action, params, callback, method, contentType) {
    var options = {
      hostname: 'localhost',
      port: 3000,
      path: '/api/v1/' + action.toString(),
      method: method,
      headers: {
        'Content-Type': contentType,
        'Content-Length': Buffer.byteLength(params)
      }
    };

    return new Promise( (resolve, reject) => {

      var req = http.request(options, 
        function(response) {
          response.setEncoding('utf8');

          var data = '';
          response.on('data', function(chunk) {
            data += chunk;
          });

          response.on('end', function() {
            var parsedResponse;

            try {
              parsedResponse = JSON.parse(data);
            } catch(err) {
              reject({message: `Invalid response from hurricane for ${action}`});
              return;
            }

            if (parsedResponse.error)
              reject(parsedResponse.error);
            else
              resolve(parsedResponse);
          });

          response.on('error', function(err){
            console.log(err.message);
            reject(err);
          });
        });

      req.on('error', function(err) {
        console.log(err);
        reject({message: err.message});
      });

      req.write(params);
      req.end(); 
    });    
}

And here is the test:

这是测试:

var http = require('http');
var expect = require('chai').expect;
var sinon = require('sinon');
var PassThrough = require('stream').PassThrough;

describe('Hurricane Client tests', function() {
  before(function() {
    this.request = sinon.stub(http, 'request');
  });

  after(function() {
    http.request.restore();
  });

  it('should convert get result to object', function(done) {
    var expected = { hello: 'world' };
    var response = new PassThrough();
    response.statusCode = 200;
    response.headers = {}
    response.write(JSON.stringify(expected));
    response.end();

    var request = new PassThrough();

    this.request.callsArgWith(1, response).returns(request);

    client.get('any', {}, null, function(result) {
      expect(result).to.eql(expected);
      done();
    });
  });
});