Les tests de fonctions asynchrones avec moka
je veux tester une fonction javascript asynchrone qui tourne en noeud.js et fait une simple demande à un 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, c'est qu'aucun code après:
var req = protocol.request(options, function(res) {
n'est pas exécuté même sur une simple console.journal.
quelqu'un Peut-il aider?
3 réponses
Vous devez spécifier le rappel done
comme argument à la fonction qui est fournie à mocha - dans ce cas, le it()
fonction. Comme ceci:
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
});
});
});
});
aussi, la signature de doRequest(options, callback)
spécifie deux arguments bien que lorsque vous l'appelez dans le test vous en fournissez trois.
Mocha probablement ne pouvait pas trouver la méthode doRequest(arg1,arg2,arg3)
.
il n'A pas fournir certains sortie d'erreur? Peut-être que vous pouvez changer les options moka pour obtenir plus information.
EDIT :
et qui a raison, la seconde affirmation serait appelée en parallèle à assert.doesNotThrow
alors qu'il ne devrait être appelé que dans le succès callback.
j'ai corrigé le code d'exemple.
EDIT 2:
Ou, pour simplifier la gestion des erreurs (voir Dan M. le commentaire):
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 supporte pas les callbacks, ou si vous pensez qu'utiliser des callbacks inutiles l'est... inutile, alors vous pouvez simplement transformer le test en test asynchrone.
au lieu de:
it('should be able to do something', function () {});
il suffit de faire:
it('should be able to do something', async function () {});
^^^^^
Maintenant vous pouvez await
fonctions async:
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 similaire dans mon projet pour un client http. Je colle le code ici et l'espoir est utile. Voici le client (Mon serveur nodejs utilise express et j'utilise promise for error handling):
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();
});
});
});