0

I am trying to write a test for the following function.

function services(api){
    request(`${api}?action=services`, function(err, res, body) {
        if (!err && res.statusCode === 200){
            var resJson = JSON.parse(body);
            var numberOfServices = resJson.length;
            console.log("Service called: services");
            console.log("-----------");
            for (i = 0; i < numberOfServices; i++){
                console.log("Service ID: " + resJson[i].service);
                console.log("Service Name: " + resJson[i].name);
                console.log("-----------");
            }
            return resJson;
        }
    }); 
}

The test is checking to see if the function returns an object. resJson is the object being returned and tested.

Below is the test case written using Mocha.js and Chai.js assertion library.

var chai = require('chai');
var assert = chai.assert;
var sendRequest = require('../request');

describe('Test 1', function() {

    var api = 'http://instant-fans.com/api/v2';

    it('services() should return an object of services', function(done) {
        var object = sendRequest.services(api);
        assert.isObject(object);
    });

});

However, when I run the test it fails with the following console output. Saying that resJson is undefined. I'm guessing that Mocha is trying to assert that resJson is an object BEFORE the function services() returns the object, but I am not sure how to resolve this.

 Test 1
    1) services() should return an object of services

 0 passing (27ms)
 1 failing

  1) Test 1 services() should return an object of services:
     AssertionError: expected undefined to be an object
      at Function.assert.isObject (node_modules/chai/lib/chai/interface/assert.js:555:35)
      at Context.<anonymous> (test/requestTest.js:11:16)

I have tried to search this online, I have seen people resolve this using done() method. However in my case that does not work due to the fact I am using a callback inside my services() function.

Ryan_
  • 64
  • 1
  • 9

2 Answers2

1

Your code looks like it is doing an asynchronous request sendRequest.services(api);

What you need to do is handle the done() callback that is made available in the mocha test: it('services() should return an object of services', function(done) {})

What you need to do is re-write your services function because it is incorrect. You cannot return from an asynchronous function, async functions are generally I/O based and do not block the main Nodejs thread. In order for your main thread to access the value you have to pass in a callback function that will be executed once the value is made available.

function services(api, cb){
    request(`${api}?action=services`, function(err, res, body) {
        var resJson;
        if (!err && res.statusCode === 200){
            resJson = JSON.parse(body);
            var numberOfServices = resJson.length;
            console.log("Service called: services");
            console.log("-----------");
            for (i = 0; i < numberOfServices; i++){
                console.log("Service ID: " + resJson[i].service);
                console.log("Service Name: " + resJson[i].name);
                console.log("-----------");
            }
        }
        if (cb) {
            cb(err, resJson);
        }
    }); 
}

And rewrite your test like so:

describe('Test 1', function() {
    var api = 'http://instant-fans.com/api/v2';

    it('services() should return an object of services', function(done) {
        sendRequest.services(api, function(resJson) {
            assert.isObject(resJson);
            done();
        });
    });

});

I would go even further and pass in the error to the callback function (this is common practice in nodejs):

cb(err, resJson);

And your test can print the error for better debugging:

sendRequest.services(api, function(err, resJson) {
    if (err) {
        console.error(err);
    }
    assert.isObject(resJson);
    done();
});
AntonB
  • 2,724
  • 1
  • 31
  • 39
0

I suggest you read this question concerning usage of asynchronous functions


You can't assign asynchronous function call to variable just as you do in var object = sendRequest.services(api). You need to use callback, which should be added as a parameter to services function

function services(api, callback){
    // your logic here
}

And instead performing return resJson you can call callback(resJson) in the services function. Then, in your test, you need to call the services with second parameter being a function(result)

sendRequest.services(api, function(result){
    assert.isObject(result);
});

EDIT

This is how your services function should look like

function services(api, callback){
    request(`${api}?action=services`, function(err, res, body) {
        if (!err && res.statusCode === 200){
            var resJson = JSON.parse(body);
            var numberOfServices = resJson.length;
            console.log("Service called: services");
            console.log("-----------");
            for (i = 0; i < numberOfServices; i++){
                console.log("Service ID: " + resJson[i].service);
                console.log("Service Name: " + resJson[i].name);
                console.log("-----------");
            }
            callback(resJson);
        }
    }); 
}
Community
  • 1
  • 1
piotrbienias
  • 7,201
  • 1
  • 28
  • 33
  • Am I going to need to make changes to the `services()` function? – Ryan_ Feb 17 '17 at 14:35
  • Yes, as I said, instead doing `return resJson` you should do `callback(resJson)`. That will ensure you that the assertion is done. I edited the answer with updated `services` function – piotrbienias Feb 17 '17 at 14:36
  • Ah that's the first time i've come across that, thank-you! :) – Ryan_ Feb 17 '17 at 14:45