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:
- 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.
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
- Testing
nodejs
Applications book - Stubbing HTTP Requests
- How to add global variables used by all tests in Javascript? StackOverflow Question
- Problem resetting global object. Bug#1 about adding should on global object
- Problem resetting global object. Bug#2 How to make expect/should/assert be global in test files and be able to pass
eslint
expressjs
Request/Response mocking utility ~ mock-express-request- HTTP Response assertions utility ~ chai-http