How to Mock HTTP Request and Response ~ Integration testing use case.

Mocking HTTP requests, for that matter responses, is essential in most unit test scenarios. Depending on the depth we want the mock to kick in, this task can become quite a feat on its own. In this article, we revisit some techniques that can make our life easy when mocking requests in integration testing scenarios.

This article is a followup to How to Mock HTTP Request and Response

In this article we will talk about:

Even though this blog post was designed to offer complementary materials to those who bought my Testing nodejs Applications book, the content can help any software developer to tuneup working environment. You use this link to buy the book. Testing nodejs Applications Book Cover

Show me the code

//
var User = require('./models').User; 
module.exports = function getProfile(req, res, next){
  User.findById(req.params.id, function(error, user){
    if(error) return next(error);
    return res.status(200).json(user);
  });
};

//Router that Authentication Middleware
var router = require('express').Router();
var authenticated = require('./middleware/authenticated');
var getUsers = require('./users/get-user');
router.get('/users/:id', authenticated, getUser);
module.exports = router;

Example:

What can possibly go wrong?

Some challenges associated with stubbing HTTP requests:

Show me the tests

The next section has the following traits backed in:

// Add promise support if this does not exist natively.
if (!global.Promise) {
    global.Promise = require('q');//or any other promise library 
}

var chai = require('chai'),
  chaiHttp = require('chai-http'),
  chai.use(chaiHttp), //registering the plugin.
  //use this line to retain cookies instead 
  agent = chai.request.agent(app),
  //agent.post()|agent.get()|agent.del()|agent.put 
  app = require('express').Router(),
  //mounting app to routes to be tested
  require('./lib/routes')(app);

//initialization of app can be express or other HTTP compatible server.
it('works', function(done){
    chai.request(app)
    .put('/user/me')//.post|get|delete
    .send({ password: '123', confirm: '123' })
    .end(function (err, res) {
        expect(err).to.be.null;
        expect(res).to.have.status(200);
        //more possible assertion 
        expect(res).to.have.status(200);
        expect(req).to.have.header('x-api-key');
        expect(req).to.have.headers;//Assert that a Response or Request object has headers.
        expect(req).to.be.json;//.html|.text 
        expect(res).to.redirect;//.to.not.redirect
        expect(req).to.have.param('orderby');//test sent parameters
        expect(req).to.have.param('orderby', 'date');//test sent parameters values 
        expect(req).to.have.cookie('session_id');//test cookie parameters
    });
});

//keeping port open 
var requester = chai.request(app).keepOpen();
it('works - parallel requests', function(){
    Promise.all([requester.get('/a'), requester.get('/b')])
    .then(responses => { /**do - more assertions here */})
    .then(() => requester.close());
});

This strategy has not been tested on routes that read/write streams.

To the question: When does it make sense to mock both Request and Response, the answer is it depends. In the event where were are interested in replicating interactions with a third party system via Requests/Responses, then it makes sense to mock request/responses.

Conclusion

In this article, we established the difference between Mocking versus Stubbing HTTP requests.

We also established the cost associated with HTTP request every time a test is executed.

With this knowledge, we reviewed ways to reduce costs by strategically stubbing HTTP read/write operations to make tests fail fast, without losing test effectiveness. There are additional complimentary materials in the “Testing nodejs applications” book.

References

#snippets #http #request #response #mocking #stubbing