Is there convenient way to mock the HTTP Request and Response objects for unit testing middlewares?
It looks like both https://github.com/howardabrams/node-mocks-http and https://github.com/vojtajina/node-mocks can be used to create mock http.ServerRequest and http.ServerResponse objects.
From the tag, it looks like this question is about Express. In that case, supertest is very good:
var request = require('supertest')
, express = require('express');
var app = express();
app.get('/user', function(req, res){
res.send(201, { name: 'tobi' });
});
request(app)
.get('/user')
.expect('Content-Type', /json/)
.expect('Content-Length', '20')
.expect(201)
.end(function(err, res){
if (err) throw err;
});
For general Node use, Flatiron Nock looks like a good option:
var nock = require('nock');
var example = nock('http://example.com')
.get('/foo')
.reply(200, { foo: 'bar' });
var http = require('http');
var options = {
host: 'example.com',
port: 80,
path: '/foo',
method: 'GET'
}
var req = http.request(options, function(res) {
res.on('data', function(chunk) {
console.log('BODY: ' + chunk);
});
});
req.on('error', function(e) {
console.log('error: ' + e);
});
req.end();
Output:
BODY: {"foo":"bar"}
i'm using nodejutsu mock:
https://github.com/nodejitsu/mock-request
Maybe this is what you are looking for.
I wrote a library to mock out the responses of requests made via standard HTTP or via the request model:
https://github.com/ctide/fakeweb
Check out https://github.com/timsavery/node-hmock or npm install hmock...any feedback welcome! The solution has worked well for me thus far.
Mockery looks great for this.
Essentially it hijacks require calls, and returns a different object/function stub that you specify.
Belatedly, if you're looking only to unit test the handler, you could inject express.Request and express.Response objects into the request function, or in this case, your middleware.
One that seemed to provide the minimal methods while keeping it simple was, for me, #jest-mock/express
If you're using supertest or nock, you're doing an integration test which can couple multiple tests. I'd also look into how it works internally because it is going be a pain to debug once it stops working.
it('should call next',
async () => {
const req = getMockReq({
headers: {
'user-agent': 'Chrome',
},
path: '/path',
})
const{res, next, clearMockRes} = getMockRes({})
await middleware(req, res, next)
expect(res.send).not.toHaveBeenCalledWith()
expect(next).toHaveBeenCalled()
})
I do not recommend this solution for real world usage, but this is the hack I used just to avoid exceptions in an experiment.
const mock_response_obj = {
statusCode: null,
setHeader: () => {},
writeHead: () => {},
end: () => {},
}
Obviously extend it as needed. Sharing in case anyone else is looking for a simple stopgap.
I encourage you to use motty. why do we need an another code?
Related
I am trying to use nock to intercept a call from my app to the internet.
The goal here is to avoid using a variable external API when testing.
What I do is :
describe('My awesome test', () => {
beforeEach(() => {
let scope = nock('http://www.myexternalapi.eu')
.log(console.log)
.post('/my/awesome/path')
.query(true)
.reply(200, response);
console.error('active mocks: %j', scope.activeMocks())
});
it('Should try to call my API but return always the same stuff ', () =>{
myService.doStuffWithAHttpRequest('value', (success) => {
// The answer must always be the same !
console.log(success);
});
})
// Other tests...
}
and myService.doStuffWithAHttpRequest('value', (success) is something like that :
const body = "mybodyvalues";
const options = {
hostname: 'myexternalapi.eu',
path: '/my/awesome/path',
method: 'POST',
headers: {
'Content-Type': 'application/xml'
}
};
const request = http.request(options, (response) => {
let body = "";
response.setEncoding('utf8');
response.on('data', data => {
body += data;
});
response.on('end', () => {
parser.parseString(body, (error, result) => {
// Do a lot of cool stuff
onSuccess(aVarFromAllTheCoolStuff);
});
});
});
When runing my tests, nock display this :
active mocks: ["POST http://www.myexternalapi.eu:80/my/awesome/path/"]
Seems good ! But my request is not matched and the external API is always called !
I have tried :
beforeEach(() => {
let scope = nock('http://www.myexternalapi.eu/my/awesome/path')
.log(console.log)
.post('/')
.query(true)
.reply(200, response);
console.error('active mocks: %j', scope.activeMocks())
});
It don't work neither.
beforeEach(() => {
let scope = nock('myexternalapi.eu')
.log(console.log)
.post('/my/awesome/path')
.query(true)
.reply(200, response);
console.error('active mocks: %j', scope.activeMocks())
});
It don't work neither and display a weird URL :
active mocks: ["POST null//null:443myexternalapi.eu:80/my/awesome/path/"]
Plus something is weird :
Nock can log matches if you pass in a log function like this:
.log(console.log)
Do not display anything... ?! Any idea ?
Thanks you, I'm going crazy with this...
The values you're providing to nock and post are not quite right.
Try this.
let scope = nock('http://www.myexternalapi.eu')
.log(console.log)
.post('/my/awesome/path')
.reply(200, response);
The string argument passed to nock must be the origin, host and protocol, and must not include any path or search param/query info.
Likewise, the post method should receive the path of the call.
One helpful tool when trying to determine why Nock isn't matching a request is to debug which Nock has integrated. So however you're running your tests, prepend DEBUG=nock*.
i am using Jest to test my code.
What i want achieve is to test redirection from http to https. (if it exists if process.env.IS_PRODUCTION).
I don't know how to test it, how to mockup this and so on...
I've tried standard get reqest but don't know how to mockup environment varible or test it in different way
it('should redirect from http to https, (done) => {
request(server)
.get('/')
.expect(301)
.end((err, res) => {
if (err) return done(err);
expect(res.text).toBe('...')
return done();
});
}, 5000);
I expect to be able to test this redirection :)
You could use the node-mocks-http libary which allows you to simulate a request and response object.
Example:
const request = httpMocks.createRequest({
method: 'POST',
url: '/',
});
const response = httpMocks.createResponse();
middlewareThatHandlesRedirect(request, response);
I never worked with jest but I believe that you can check the response.location parameter once the middleware has been called
Preface: I'm not familiar with jest or express or node. But I have found it to be much easier to test explicit configuration (instantiating objects with explicit values) vs implicit configuration (environmental variables and implementation switches on them):
I'm not sure what request or server are but explicit approach might look like:
it('should redirect from http to https, (done) => {
const server = new Server({
redirect_http_to_https: true,
});
request(server)
.get('/')
.expect(301)
.end((err, res) => {
if (err) return done(err);
expect(res.text).toBe('...')
return done();
});
}, 5000);
This allows the test to explicitly configure server to the state it needs instead of mucking with the environment.
This approach also helps to keep process configuration at the top level of your application:
const server = new Server({
redirect_http_to_https: process.env.IS_PRODUCTION,
});
I'm aware that there are several questions related to mine, but I didn't find any of them useful:
this one doesn't apply to my case, I'm actually getting the answer, it's the contents that I can't get.
on this one, on the other hand, the problem is a wrong handling of an asynchronous call, which is not my case
there, well, I really didn't fully understand this question
And so on...
Then, I think this is a legitimate question. I'm actually performing some encryption in my server (express routing in node) through a post request:
app.post('/encrypt', encrypt);
Encrypt is doing:
function encrypt(req,res) {
if(req.body.key && req.body.message) {
var encryptedMessage = Encrypter.encrypt(req.body.key,req.body.message);
return res.status(200).json({ message: encryptedMessage });
}
res.status(409).json({ message: 'the message could not be encrypted, no key found' });
}
}
So, I tested this via console.log, and it's working. When the server receives the request, the encrypted message is being generated.
At the same time, I'm testing my thing with mocha and I'm doing it like so:
describe('# Here is where the fun starts ', function () {
/**
* Start and stop the server
*/
before(function () {
server.listen(port);
});
after(function () {
server.close();
});
it('Requesting an encrypted message', function(done) {
var postData = querystring.stringify({
key : key,
message : message
});
var options = {
hostname: hostname,
port: port,
path: '/encrypt',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postData.length
}
};
var req = http.request(options, function(res) {
res.statusCode.should.equal(200);
var encryptedMessage = res.message;
encryptedMessage.should.not.equal(message);
done();
});
req.on('error', function(e) {
//I'm aware should.fail doesn't work like this
should.fail('problem with request: ' + e.message);
});
req.write(postData);
req.end();
});
});
So, whenever I execute the tests, it fails with Uncaught TypeError: Cannot read property 'should' of undefined because res.message does not exist.
None of the res.on (data, end, events is working, so I suppose the data should be available from there. First I had this:
var req = http.request(options, function(res) {
res.statusCode.should.equal(200);
var encryptedMessage;
res.on('data', function (chunk) {
console.log('BODY: ' + chunk);
encryptedMessage = chunk.message;
});
encryptedMessage.should.not.equal(message);
done();
});
But res.on was never accessed (the console.log didn't show anything). I'm therefore a bit stuck here. I'm surely doing some basic stuff wrong, but I don't have a clue, and the many questions I found doesn't seem to apply to my case.
Weird enough, if I launch a test server and then I curl it
curl --data "key=secret&message=veryimportantstuffiabsolutellyneedtoprotect" localhost:2409/encrypt
Curl justs waits ad aeternam.
Actually I was doing it properly at the beginning, and the problem was indeed the same than in the second question I mentionned I was actually "clearing" my context with done() before the post data arrived. The solution is:
var req = http.request(options, function(res) {
res.statusCode.should.equal(200);
res.on('data', function(data) {
encryptedMessage = JSON.parse(data).message;
encryptedMessage.should.not.equal(message);
done();
});
});
In such a way that done() is only called when the data has been threated. Otherwise, mocha will not wait for the answer.
I have some CRUD controllers set up in my project like this:
var getMaps = function (req, res) {
Map.find({}).exec(function (err, collections) {
res.send(collections);
});
};
In order to use these in the server side code, I've been using node's http.get like this:
var options = {
host: 'localhost',
port: 3030,
path: '/api/map'
};
http.get(options, function (res) {
var data = '';
res.on("data", function (chunk) {
data += chunk;
}).on('end', function () {
data = JSON.parse(data);
console.log(data);
});
}).on('error', function (e) {
console.log("Got error: " + e.message);
});
where my routes file contains
app.get('/api/map', map.getMaps);
Is this the correct way of doing it? It seems like it would be slow since it is an http request but I don't know any other way to do it
Better you go through $http and $resource links once to have a better understanding of angular's APIs.
You can also try out the MEANs CRUD module which comes by default when you generate a MEAN application.
I am having trouble unit testing with Express on a number of fronts, seems to be a lack of documentation and general info online about it.
So far I have found out I can test my routes with a library called supertest (https://github.com/visionmedia/superagent), but what if I have broken my routes and controllers up, how can I go about testing my controllers independently of their routes.
here is my test:
describe("Products Controller", function() {
it("should add a new product to the mongo database", function(next) {
var ProductController = require('../../controllers/products');
var Product = require('../../models/product.js');
var req = {
params: {
name: 'Coolest Product Ever',
description: 'A very nice product'
}
};
ProductController.create(req, res);
});
});
req is easy enough to mockup. res not so much, I tried grabbing express.response, hoping I could just inject it but this hasn't worked. Is there a way to simulate the res.send object? Or am I going the wrong way about this?
When you are testing your routes, you don't actually use the inbuilt functions. Say for example, ProductController.create(req, res);
What you basically need to do is, run the server on a port and send a request for each url. As you mentioned supergent, you can follow this code.
describe("Products Controller", function() {
it("should add a new product to the mongo database", function(next) {
const request = require('superagent');
request.post('http://localhost/yourURL/products')
.query({ name: 'Coolest Product Ever', description: 'A very nice product' })
.set('Accept', 'application/json')
.end(function(err, res){
if (err || !res.ok) {
alert('Oh no! error');
} else {
alert('yay got ' + JSON.stringify(res.body));
}
});
});
});
You can refer to superagent request examples here.