Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing asynchronous function with mocha

I want to test a asynchronous javascript function that runs in node.js and makes a simple request to a 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:

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.

Can anybody help?

like image 530
MadFool Avatar asked Aug 28 '12 13:08

MadFool


People also ask

What is async Mocha?

Mocha supports async functions out of the box, no plugins or configuration needed. You can pass an async function to it() , and Mocha will handle any errors that occur. describe('get()', function() { it('works', async function() { const res = await get('http://httpbin.org/get?

How do you know if a function is async?

To check if a function is async, access the constructor.name property on the function and check if the value is equal to AsyncFunction , e.g. myFunction.constructor.name === 'AsyncFunction' . If the equality check returns true , then the function is async.


2 Answers

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

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.

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

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

EDIT :

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

I have fixed the example code.

EDIT 2:

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

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);         });     }); }); 
like image 118
Risadinha Avatar answered Sep 20 '22 16:09

Risadinha


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 await async functions:

it('should be able to do something', async function () {   this.timeout(40000);    var result = await someComplexFunction();    assert.isBelow(result, 3); }); 
like image 33
Thomas Wagenaar Avatar answered Sep 22 '22 16:09

Thomas Wagenaar