Search code examples
unit-testingmocha.jssupertestnocksupertest-as-promised

testing multiple http request using mocha


I've been trying to solve this issue for days; create the test for this case using mocha:

app.post('/approval', function(req, response){
request.post('https://git.ecommchannel.com/api/v4/users/' + req.body.content.id + '/' + req.body.content.state + '?private_token=blabla', function (error, resp, body) {
    if (resp.statusCode == 201) {
                //do something
            } else {
                response.send("failed"), response.end();
            }
        });  
    } else {
        response.send("failed"), response.end();
    }
});

});

I've tried several ways, using supertest to test the '/approval' and using nock to test the post request to git api. But it always turn "statusCode" is undefined. I think that's because the request to git api in index.js is not inside a certain function(?) So I can't implement something like this : https://codeburst.io/testing-mocking-http-requests-with-nock-480e3f164851 or https://scotch.io/tutorials/nodejs-tests-mocking-http-requests

    const nockingGit = () => {
    nock('https://git.ecommchannel.com/api/v4/users')
        .post('/1/yes', 'private_token=blabla')
        .reply(201, { "statusCode": 201 });
};

it('approval', (done) => {
let req = {
    content: {
        id: 1,
        state: 'yes'
    },
    _id: 1
}
request(_import.app)
    .post('/approval')
    .send(req)
    .expect(200)
    .expect('Content-Type', /html/)
    .end(function (err, res) {
        if (!err) {
            nockingGit();  
        } else {
            done(err);
        }
    });
done();

})

Then I tried to use supertest as promise

    it('approve-block-using-promise', () => {
       return promise(_import.app)
        .post('/approval')
        .send(req = {
            content: {
                id: 1,
                state: 'yes'
            },
            _id: 1
        })
        .expect(200)
        .then(function(res){
            return promise(_import.app)
            .post("https://git.ecommchannel.com/api/v4/users/")
            .send('1/yes', 'private_token=blabla')
            .expect(201);
        })
})

But it gives error: ECONNEREFUSED: Connection refused. I didn't find any solution to solve the error. Some sources said that it needs done() .. but it gives another error message, 'ensure "done()" is called" >.<

So then I've found another way, using async (https://code-examples.net/en/q/141ce32)

    it('should respond to only certain methods', function(done) {
    async.series([
        function(cb) { request(_import.app).post('/approval')
        .send(req = {
            content: {
                id: 1,
                state: 'yes'
            },
            _id: 1
        })
        .expect(200, cb); },
        function(cb) { request(_import.app).post('/https://git.ecommchannel.com/api/v4/users/').send('1/yes', 'private_token=blabla').expect(201, cb); },
    ], done);
});

and it gives this error : expected 201 "Created", got 404 "Not Found". Well, if I open https://git.ecommchannel.com/api/v4/users/1/yes?private_token=blabla in the browser it does return 404. But what I expect is I've injected the response to 201 from the unit test; so whatever the actual response is, the statusCode suppose to be 201, right? But then since it gives that error, is it means the unit test really send the request to the api? Pls help me to solve this; how to test the first code I shared. I really new into unit test.


Solution

  • There are a few things wrong with your posted code, I'll try to list them out but I'm also including a full, passing example below.

    First off, your call to git.ecommchannel in the controller, it's a POST with no body. While this isn't causing the errors you're seeing and is technically not incorrect, it is odd. So you should double check what the data you should be sending is.

    Next, I'm assuming this was a copy/paste issue when you created the question, but the callback for the request in your controller is not valid JS. The brackets don't match up and the send "failed" is there twice.

    Your Nock setup had two issues. First the argument to nock should only have origin, none of the path. So /api/v4/users had to be moved into the first argument of the post method. The other issue was with the second argument passed to post that is an optional match of the POST body. As stated above, you aren't currently sending a body so Nock will always fail to match and replace that request. In the example below, the private_token has been moved to match against the query string of the request, as that what was shown as happening.

    The calling of nockingGit was happening too late. Nock needs to register the mock before you use Supertest to call your Express app. You have it being called in the end method, by that time it's too late.

    The test labeled approve-block-using-promise has an issue with the second call to the app. It's calling post via Supertest on the Express app, however, the first argument to that post method is the path of the request you're making to your app. It has nothing to do with the call to git.ecommchannel. So in that case your Express app should have returned a 404 Not Found.

    const express = require('express')
    const nock = require('nock')
    const request = require('request')
    const supertest = require('supertest')
    
    const app = express()
    app.use(express.json())
    
    app.post('/approval', function(req, response) {
      const url = 'https://git.ecommchannel.com/api/v4/users/' + req.body.content.id + '/' + req.body.content.state
      request.post({
          url,
          qs: {private_token: 'blabla'}
          // body: {} // no body?
        },
        function(error, resp, body) {
          if (error) {
            response.status(500).json({message: error.message})
          } else if (resp.statusCode === 201) {
            response.status(200).send("OK")
          } else {
            response.status(500).send("failed").end();
          }
        });
    });
    
    const nockingGit = () => {
      nock('https://git.ecommchannel.com')
        .post('/api/v4/users/1/yes')
        .query({private_token: 'blabla'})
        .reply(201, {"data": "hello world"});
    };
    
    it('approval', (done) => {
      const reqPayload = {
        content: {
          id: 1,
          state: 'yes'
        },
        _id: 1
      }
    
      nockingGit();
    
      supertest(app)
        .post('/approval')
        .send(reqPayload)
        .expect(200)
        .expect('Content-Type', /html/)
        .end(function(err) {
          done(err);
        })
    })