web-dev-qa-db-fra.com

Test de la fonction asynchrone avec le moka

Je souhaite tester une fonction javascript asynchrone qui s'exécute dans node.js et effectue une requête simple auprès d'une API http:

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);
}

Exécuté, ce code fonctionne et la réponse sera affichée comme prévu.

Si j'exécute ceci dans un test mocha, la requête n'est pas exécutée:

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 ... }');
    });
});

Le problème est qu’aucun code après:

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

est exécuté même pas un simple console.log.

Quelqu'un peut aider?

27
MadFool

Vous devez spécifier le callback done comme argument de la fonction fournie à mocha, dans ce cas la fonction it(). Ainsi:

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
            });
        });
    });
});

En outre, la signature de doRequest(options, callback) spécifie deux arguments, mais vous en fournissez trois lorsque vous l’appelez dans le test. 

Mocha n'a probablement pas trouvé la méthode doRequest(arg1,arg2,arg3)

N'a-t-il pas fourni une sortie d'erreur? Peut-être que vous pouvez changer les options de moka pour obtenir plus d’informations.

MODIFIER :

andho a raison, la deuxième assertion serait appelée parallèlement à assert.doesNotThrow alors qu'elle ne devrait être appelée que dans le rappel de succès. 

J'ai corrigé l'exemple de code.

EDIT 2:

Ou, pour simplifier la gestion des erreurs (voir le commentaire de 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);
        });
    });
});
45
Risadinha

Si vous avez une fonction asynchrone qui ne prend pas en charge les rappels, ou si vous pensez que les rappels inutiles sont ... inutiles, vous pouvez simplement transformer le test en test asynchrone.

au lieu de:

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

faites simplement:

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

Maintenant, vous pouvez await fonctions asynchrones:

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

  var result = await someComplexFunction();

  assert.isBelow(result, 3);
});
2
Thomas W

J'ai fait un test très similaire dans mon projet pour un client http. Je colle le code ici et j'espère que c'est utile . Voici le client (mon serveur nodejs utilise express et j'utilise promesse pour la gestion des erreurs):

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(); 
    });    
}

Et voici le 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();
    });
  });
});
0
Antonio Ganci