Test della funzione asincrona con moka

Voglio testare una funzione javascript asincrona che viene eseguita in node.js e fa una semplice richiesta ad un 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); } 

Eseguito questo codice funziona e la risposta verrà visualizzata come previsto.

Se eseguo questo in un test di moka, la richiesta non viene eseguita:

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

Il problema è che nessun codice dopo:

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

viene eseguito nemmeno un semplice console.log.

Qualcuno può aiutare?

Devi specificare il callback done come argomento della funzione fornita a mocha, in questo caso la funzione it() . Così:

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

Inoltre, la firma di doRequest(options, callback) specifica due argomenti anche se quando la chiami nel test ne fornisci tre.

Mocha probabilmente non è riuscito a trovare il metodo doRequest(arg1,arg2,arg3) .

Non ha fornito qualche output di errore? Forse puoi cambiare le opzioni di moka per ottenere più informazioni.

MODIFICARE :

eho ha ragione, la seconda affermazione verrebbe chiamata in parallelo a assert.doesNotThrow mentre dovrebbe essere chiamata solo nel callback di successo.

Ho corretto il codice di esempio.

MODIFICA 2:

Oppure, per semplificare la gestione degli errori (vedere il commento di 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); }); }); }); 

Se si dispone di una funzione asincrona che non supporta i callback, o se si ritiene che l’uso di callback non necessari sia … inutile, è anche ansible trasformare il test in un test asincrono.

invece di:

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

semplicemente fai:

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

Ora puoi await funzioni asincrone:

 it('should be able to do something', async function () { this.timeout(40000); var result = await someComplexFunction(); assert.isBelow(results, 3); }); 

Ho fatto un test molto simile nel mio progetto per un client http. Incollo il codice qui e spero sia utile. Ecco il client (il mio server nodejs usa express e io uso per la gestione degli errori):

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

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