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