Simple Engineering

http

The depth of an HTTP request or response mock brings a level of complexity to the whole system. In this article, we revisit some techniques used to mock HTTP request/response when used in the same test case.

In this article we will talk about:

  • Mocking Request Objects
  • Mocking Response Objects
  • Mocking Request and Response object in the same test case.
  • When does it make sense to mock both Request and Response.

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

module.exports.getUsers = function getUsers(req, res, next){
  UserModel.find(req.params, (error, users){
    if(error) return next(error, null);
    return res.status(200).json(users);
  });
}

Example: in controller/get-users.js

What can possibly go wrong?

When trying to figure out how to approach mocking request and response objects, the following points may be a challenge:

  • Stubbing the right request/response methods
  • Mock output that can be consumed by the other callers
  • Stubbing request/response handlers in the same test case
  • Strategic mocking that can make a live server obsolete

How to mock Request/Response Objects the easy way

Testing an expressjs middleware provides a good use case where mocking a request and response in the same test case makes sense.

Key objectives are:

  • Spying if certain calls have been called
  • Make sure the requests don't leave the local machine.
var sinon = require('sinon'),
    chai = require('chai'),
    expect = chai.expect,
    getUsers = require('./controller').getUsers;

describe("getUsers()", function() {
  it("should guarantee a response", function() {
    var req  = {}, 
      res  = { send: sinon.spy()}, 
      next = sinon.spy();
    getUsers(req, res, next);
    expect(res.send.calledOnce).to.equal(true);
    res.send.restore(); 
  });     
});

code excerpt adapted from – Unit Testing Controllers the Easy Way in Express 4

Particular Case: How to mock a response that uses a streaming, or other hard to mock interfaces. Keyword: let the flow intact, but fake read/write data instead.

Mocking request

Request object provided by node-mocks-http is pretty similar to the request provided by the native http found in nodejs library

var request;
//When method = GET|DELETE
request = httpMock.createRequest({method: method, url: url});

//When method = PUT|POST
var request = httpMock.createRequest({method, url, body: body})

Mocking Response

//initialization(or beforeEach)
var response = httpMock.createResponse({
    eventEmitter: require('events').EventEmitter
});

//Usage: somewhere in tests
let next = sinon.spy();
getUsers(request, response, next);
response.on('end|data|error', function(error){
  //write tests in this close.
});

Using node-mocks-http is in the gray area of integration testing. However, this technique can be verifiable in use cases where the first strategy falls short.

There is more on integration testing mocking strategy: How to Mock HTTP Request and Response ~ Integration testing use case

Conclusion

In this article, we revisited strategies to mock HTTP Request and Response methods in the same test case, while using mock data to emulate interaction with remote systems. We also re-iterated the difference between stubbing and mocking, and how spies(fake) fall into the testing big picture. There are additional complimentary materials in the “Testing nodejs applications” book on this very same subject.

References

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

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:

  • Stubbing HTTP Request Objects
  • Mocking Request and Response object in the same test case.
  • When does it make sense to mock both Request and Response.

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:

  • How deep a stub should go

Show me the tests

The next section has the following traits backed in:

  • When to use: Testing all routes at once
  • When to use: Asserting on nature of the response output
  • When not to use: When running unit testing
  • When to use: When running integration tests
// 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