I have some middleware functions in my Express app which make outgoing request to a separate server and then send the response returned back to the client.
So this kinda thing:
var request = require('request'),
helpers = require('./helpers.js');
//GET
module.exports.fetch = function(req, res) {
var options = helpers.buildAPIRequestOptions(req);
request(options, function(err, response, body){
res.status(response.statusCode).send(body);
});
};
I am looking at writing unit tests and would like to mock out and return my own responses when the outgoing requests withing these functions gets fired.
I have come across supertest and have had a go at writing a suite like so:
var app = require('./index.js'),
request = require('supertest')(app),
should = require('should');
//GET
describe('GET requests', function(){
it('should do something', function(done){
request
.get('/api/schemes')
.expect(200, done)
.end(function(err, res){
// console.log(res);
// console.log(err);
res.status.should.equal(200);
});
});
});
I think I need something like sinon here, so I could spy on a request that would hit my middleware function and return a response of my choosing. I am just struggling to understand how to add the stubbing to my test.
Can someone advise further please?
Thanks
node-nock can record requests made via the http module (and so, by proxy, request). These can then be queued for replaying in your test suite. The GitHub page has great documentation.
Related
I have an application which dynamically updates the client side data whenever there comes data update on the server side. I am using AngularJS as front end and NodJS as the relay server. So when there is data update happens another application of someone else would post the data to my NodeJS server, which will be handled by a NodeJS route API (eg: route.post(/send)). For the testing purpose, I am using REST CLIENT for sending data to this API, and these all are working fine too.
When I do unit testing this route(/send) I should confirm whether there happens an event emit to the front end or not when I post a valid data to the node route right?. I am using mocha chai for unit test purpose. I could send the post request to the above-mentioned API using chai.request() method.But how can I verify whether API emitted the data to the client or not?.Below is my sample code and I think it is self-explanatory.
Issue facing is: This piece of code is working, and which is the proof of the success of the event being called.
`server.on('gotData',function(){
console.log('event emitted');
assert(true);
done();
});`
Node Code:
router.post('/send', jsonParser, function (req, res) {
//some logic here
io.to(socketId).emit('gotData', data);
});
Client side
var socket = io.connect(serverUrl);
socket.on('gotData', function(data){
console.log('Data got in client side');
});
Test Suite:
it('should be sent event to client-side when valid object has provided', function (done) {
chai.request(server)
.post('/send')
.set({'Content-Type':'application/json'})
.send(data);
var socket;
socket = io.connect(server);
socket.on('gotData',function(){
console.log('event emitted'); // not get firing after chai request
assert(true);
done();
});
});
I'm not really sure what you are asking, but your tests can become more focused if they ignore the event plumbing and router plumbing and focus on the logic of your test. If you audit your router, and io framework and both have tests to reasonably verify that event emissions work then you could isolate your specific logic and only exercise it:
router.post('/send', jsonParser, function (req, res) {
//some logic here
io.to(socketId).emit('gotData', data);
});
could become:
function yourSendHandler(req, res, ioModule) {
// allow for user passed in ioModule, if not present use global
var io = ioModule || io;
//some logic here
io.to(socketId).emit('gotData', data);
}
router.post('/send', jsonParser, yourSendHandler);
Now, if you export your handler, your test can import and exercise yourSendHandler directly, without having to worry about events, you could provide a mock io in your unit test and assert on its calls.
Having your function defined anonymously in the router couples it tightly to the router and doesn't allow for easy isolation or testing.
I'm trying to build a test set for my API, which was developed with nodejs/express, with mocha/chai. Basically, the index returns a simple string that I know it's working because I can see on my browser:
router.get('/', function(req, res, next) {
res.send('Hello World from Eclipse');
});
Then I followed this tutorial to build this test:
var supertest = require("supertest");
var should = require("should");
// This agent refers to PORT where program is runninng.
var server = supertest.agent("http://localhost:5000");
// UNIT test begin
describe("SAMPLE unit test",function(){
// #1 should return home page
it("should return home page",function(done){
// calling home page api
server
.get("/")
.expect("Content-type",/json/)
.expect(200) // THis is HTTP response
.end(function(err,res){
// HTTP status should be 200
res.status.should.equal(200);
// Error key should be false.
res.body.error.should.equal(false);
done();
});
});
});
I can see on the log of my server that the address is been called, however, the test always say that it cannot read property 'should' of undefined, on the line where I have 'res.status.should.equal(200);'. Probably because 'res' is undefined. In other words, no answer from the API.
Am I missing something here? I running mocha with no arguments...
Try something like this:
var expect = require('chai').expect;
var request = require('supertest');
var uri = 'your url';
describe('Profile',function(){
it('Should return a users profile',function(done){
request
.get(uri + '/api/1.0/profile')
.query({app_id:'12345'})
.end(function(err,res){
var body = res.body;
expect(body.first_name).to.equal('Bob');
expect(body.last_name).to.equal('Smith');
done()
});
});
});
Make sure you have the correct requires included.
You should check for errors in .end():
.end(function(err, res) {
if (err) return done(err);
...
});
The test case is expecting the content type to match /json/, which it doesn't, so it should be set (and res will be undefined because of that).
Tangential to this question, I would like to find out if there is a way of triggering the Express Router without actually going through HTTP?
The Router has a "private" method named handle that accepts a request, a response, and a callback. You can take a look at the tests that Express has for its Router. One example is:
it('should support .use of other routers', function(done){
var router = new Router();
var another = new Router();
another.get('/bar', function(req, res){
res.end();
});
router.use('/foo', another);
router.handle({ url: '/foo/bar', method: 'GET' }, { end: done });
});
The Express team uses SuperTest to perform integration tests on the Router. It is my understanding that SuperTest still uses the network but they handle all of this for you so it behaves as if the tests were all in memory. SuperTest does seem to be widely used and an acceptable way to test your routes.
As an aside, you didn't say what you were attempting to test but if your goal is to test some routes, an alternative to SuperTest could be to extract the logic in your routes into a separate module that can be tested independent of Express.
change:
routes
|
-- index.js
to:
routes
|
-- index.js
|
controllers
|
-- myCustomController.js
The tests could then simply target myCustomController.js and inject any necessary dependencies.
By going to the source of Express, I was able to find out that there is indeed an API that is just as simple as I wished for. It is documented in the tests for express.Router.
/**
* #param {express.Router} router
*/
function dispatchToRouter(router, url, callback) {
var request = {
url : url,
method : 'GET'
};
// stub a Response object with a (relevant) subset of the needed
// methods, such as .json(), .status(), .send(), .end(), ...
var response = {
json : function(results) {
callback(results);
}
};
router.handle(request, response, function(err) {
console.log('These errors happened during processing: ', err);
});
}
But ... the downside is, exactly the reason why it is undocumented in the first place: it is a private function of Router.prototype:
/**
* Dispatch a req, res into the router.
* #private
*/
proto.handle = function handle(req, res, out) {
var self = this;
...
}
So relying on this code is not the safest thing in the world.
You can use run-middleware module exactly for that. You create an express app a usuaul, and then you can call the app using your parameters
it('should support .use of other routers', function(done){
var app=require('express')()
app.get('/bar', function(req, res){
res.status(200).end();
});
app.runMiddleware('/bar',{options},function(responseCode,body,headers){
console.log(responseCode) // Should return 200
done()
})
});
More info:
Module page in Github & NPM;
Examples of use run-middleware module
Disclosure: I am the maintainer & first developer of this module.
I'm using MEAN stack with MeanJs. The thing is, I have a task that requires calling a GET request from the server side (Expressjs) to another server (with a different domain name).
The code in the client side (AngularJs) calls:
$scope.getWorkflow = function() {
$http.get('/ezee', $scope.credentials).success(function(response) {
console.log(response.message);
}).error(function(response) {
console.log('error');
});
};
And the corresponding server controller function is:
exports.list = function(req, res) {
req.get('http://ezslave.io', function(q, r){
res.json({message: r.message}); // just to test
});
};
Obviously, the code below doesn't work. I'm unsure about how to make a GET request from that list function. Am I supposed to use ExpressJs or pure NodeJs for this? And how to get the correct library loaded?
Use the request module of nodejs : https://github.com/mikeal/request
for sending the http request.
var request = require("request");
exports.list = function(req, res) {
request("http://ezslave.io",function(err,response,body){
res.send(response);
});
};
Hope this helps you
I have the following...
var request = require('request');
exports.list = function(req, res){
res.send("Listing");
};
exports.get = function(req, res){
request.get("<URL>", function (err, res, body) {
if (!err) {
res.send(body,"utf8");
}
});
};
This fails with the following....
TypeError: Object #<IncomingMessage> has no method 'send'
How do I do this?
UPDATE tried to use write instead of send but...
/Users/me/Development/htp/routes/property.js:9
res.setHeader('Content-Type', 'text/html');
^
TypeError: Object #<IncomingMessage> has no method 'setHeader'
Also writing out to the console instead works fine.
Problem was with scope of variables, my response output was the same name as the response object I got back in my callback. Changing this around (resp vs res) made it work....
exports.get = function(req, res){
request.get("<url>", function (err, resp, body) {
if (!err) {
res.send(body);
}
});
};
What you are trying to do, is to make Request > Response server. But you are using Request module, that allows to get stuff rather than respond.
What you need is http or better get express.js and use it, as it is straight forward and well popular web framework for exactly what you need.
I wasn't aware OP is using Express. You will encounter a similar error if you attempt to use req.send with the vanilla HTTP module instead of Express.
var http = require('http');
function requestHandler(req, res){
//res.send(200, '<html></html>'); // not a valid method without express
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.end('<html><body>foo bar</body></html>');
};
http.createServer(handler).listen(3000);