I know this has been discussed a couple of times. Anyway, I feel like all the provided solutions don't (perfectly) fit to my requirement(s). I have the following code:
router.js:
------------------
var Router = function(app, resourceName, controller) {
//Create
app.post('/api/' + resourceName, function(req, res) {
console.log('Incoming request: ' + resourceName + ' (POST)');
controller.create(req, res);
});
};
module.exports = Router;
As you can see this is a very "generic" router. It can be instantiated for example in the server like this:
var app = express();
var userController = ...
var userRouter = new Router(app, 'Users', userController);
So I don't have to write a file per resource but I just have one generic router.
I would like to test my generic router but I see some problems:
How to "inject" the app? I could create an instance of Express (var app = express();) but I think a mock would be better (as this is a unit test, not an integration test!). What's the best way to get an appropriate mock?
What exactly should I test? As far as I see my router itself (without integration) isn't doing anything else but console output (not worth to test) and a call of a function (controller.create(req, res);). How should I test if this function is called? Or is there anything else to test?
You should probably make a stub implementation of app.
What you want to test is that the constructor registers listeners on specified routes + HTTP methods. I would advise putting Sinon.js stubs into your app stub, and then in your tests check that they are called with expected arguments.
I would use jasmine.createSpyObj to mock app (and maybe controller as well).
I think you just need to test that app.post gets called with the arguments '/api/' + resourceName and controller.create, because you aren't testing that express.post works correctly or not.
Here's how I'd do those two things specifically.
I'd modify router.js a little bit to make this easier:
var Router = function(app, resourceName, controller) {
app.post('/api/' + resourceName, controller.create.bind(controller))
}
module.exports = Router;
And then the test would look like this:
describe("Router", function() {
it("should route /api to controller.create", function() {
router = require('./router');
app = jasmine.createSpyObj('application', ['post']);
controller = jasmine.createSpyObj('controller', ['create']);
router(app, 'foo', controller);
expect(app.post).toHaveBeenCalledWith('/api/foo', jasmine.any(Function));
});
});
This isn't a perfect test because it isn't actually checking that controller.create specifically is getting called. That gets a little more complicated because of the .bind() stuff.
describe("Router", function() {
it("should route /api to controller.create", function() {
router = require('./router');
app = jasmine.createSpyObj('application', ['post']);
controller = jasmine.createSpyObj('controller', ['create']);
controller.create = jasmine.createSpyObj('binder', ['bind']);
controller.create.bind.and.returnValue('bar');
router(app, 'foo', controller);
expect(controller.create.bind).toHaveBeenCalledWith(controller);
expect(app.post).toHaveBeenCalledWith('/api/foo', controller.create.bind(controller));
});
});
Related
I have such router:
exports.read = (req,res) ->
// do stuff with DB
res.status(200).send
data:data
Now how can I use this router inside another router, call it exports.wrapper? I wnat to avoid having to rewrite my DB requests again and again. Is this approach that I have in mind recommended?
I would not recommend attempting to wrap routers inside each other.
It's recommended in Express 4 to use Express's router object like so:
// router.js
var myRouter = express.Router();
myRouter.route('/read', myController.readMethod);
Then in your controller you would handle the request and end with the result call:
// myController.js
exports.readMethod = function(req, res) {
var data = readFromDB(req.params);
res.send('read method renders', data);
}
exports.readMethod2 = function(req, res) {
var data = readFromDB(req.params);
res.send('read method 2 renders', data);
}
function readFromDB(params) {
// make a call to the DB (maybe via a model)
// return some data
}
Hope that helps
edit
Additionally, I would recommend wrapping your DB calls in a model, to abstract them away from your router or controller logic. For a reference of a well organised Express App that uses MVC checkout this Yeoman generator - https://github.com/ngenerio/generator-express-simple
second edit
In my very brief example externalising the readFromDB method to a model makes this function moot, if all it's doing is getting data from the DB put it in a model.
Coming from a non Node background, my first instinct is to define my service as such
MyService.js
module.exports = new function(dbConnection)
{
// service uses the db
}
Now, I want one open db connection per request, so I define in middleware:
res.locals.db = openDbConnection();
And in some consuming Express api code:
api.js
var MyService = require(./services/MyService')
...
router.get('/foo/:id?', function (req, res) {
var service = new MyService(res.locals.db);
});
Now, being that Node's preferred method of dependency injection is via the require(...) statement, it seems that I shouldn't be using the constructor of MyService for injection of the db.
So let's say I want to have
var db = require('db');
at the top of MyService and then use somehow like db.current....but how would I tie the db to the current res.locals object now that db is a module itself? What's a recommended way of handling this kind of thin in Node?
Updated Answer: 05/02/15
If you want to attach a DB connection to each request object, then use that connection in your service, the connection will have to be passed to myService some how. The example below shows one way of doing that. If we try to use db.current or something to that effect, we'll be storing state in our DB module. In my experience, that will lead to trouble.
Alternatively, I lay out the approach I've used (and still use) in this previous answer. What this means for this example is the following:
// api.js
var MyService = require(./services/MyService')
...
router.get('/foo/:id?', function (req, res) {
MyService.performTask(req.params.id);
});
// MyService.js
var db = require('db');
module.exports = {
performTask: function(id)
{
var connection = db.getOpenConnection();
// Do whatever you want with the connection.
}
}
With this approach, we've decoupled the DB module from the api/app/router modules and only the module that actually uses it will know it exists.
Previous Answer: 05/01/15
What you're talking about could be done using an express middleware. Here's what it might look like:
var db = require('db');
// Attach a DB connection to each request coming in
router.use(req, res, next){
req.locals.db = db.getOpenConnection();
next();
}
// Later on..
router.get('/foo/:id?', function (req, res) {
// We should now have something attached to res.locals.db!
var service = new MyService(res.locals.db);
});
I personally have never seen something like new MyService before in express applications. That doesn't mean it can't be done, but you might consider an approach like this
// router.js
var MyService = require('MyService');
router.get('/foo/:id?', function (req, res) {
MyService.foo(res.locals.db);
});
// MyService.js
module.exports.foo(connection){
// I have a connection!
}
Say I want to test a a user login controller that sends login codes via SMS with Twilio. How should I set up the test so that I can mock Twilio and see what codes it's sending back. My approach was to proxyquire the twilio client object and spy on it with sinon, but I don't think I'm getting it quite right.
controller user.js
var smsClient = new twilio.RestClient(config.get('twilio_account_sid'), config.get('twilio_auth_token'));
module.exports = {
checkCode: function(phone){
var code = getNewCode();
smsClient.sms.messages.create({
from: config.get('twilio_phone_number'),
to: phone,
body: 'Your code :' + code
}, callback);
}
}
test file
var twilioMock = //what goes here??
var smsSpy = sinon.spy(twilioMock.sms.messages, 'create');
var User = proxyquire('../models/user', { 'mongoose': mongooseMock, 'smsClient': twilioMock });
... some describe and it statements ...
twilioMock.sms.messages.should.have.been.calledOnce() //this is where I don't know what I should be checking
// or is this the right way?
//smsSpy.should.have.been.calledOnce()
I am answering this very late but this might help someone.
I haven't used proxywire but it seems very similar to rewire (just by looking at your code). You should try the following:
var twilioMock = new twilio.RestClient(config.get('twilio_account_sid'), config.get('twilio_auth_token'));
I am more used to rewire. npm i rewire --save-dev. Using rewire you may try the following: (concept remains the same)
In your test:
var rewire = require('rewire');
var twilioMock = new twilio.RestClient(config.get('twilio_account_sid'), config.get('twilio_auth_token'));
var userController = rewire('./path_to_user.js') // notice use of rewire
beforeEach(function(){
this.smsClient = twilioMock; // `this` is available within your tests
userController.__set__('smsClient', this.smsClient);
});
it('should something', sinon.test(function(){
var smsSpy = this.spy(this.smsClient.sms.messages, 'create');
}));
I’m implementing a RESTful API using Node and Express. When it comes to routing, currently it looks like this:
var cat = new CatModel();
var dog = new DogModel();
app.route('/cats').get(cat.index);
app.route('/cats/:id').get(cat.show).post(cat.new).put(cat.update);
app.route('/dogs').get(dog.index);
app.route('/dogs/:id').get(dog.show).post(dog.new).put(dog.update);
I don’t like this for two reasons:
Both cat and dog models are instantiated whether I need them or not.
I have to repeat /cats and /dogs for every path schema
I’d love to have something like this (not working, of course):
app.route('/cats', function(req, res)
{
var cat = new CatModel();
this.route('/').get(cat.index);
this.route('/:id').get(cat.show).post(cat.new).put(cat.update);
});
app.route('/dogs', function(req, res)
{
var dog = new DogModel();
this.route('/').get(dog.index);
this.route('/:id').get(dog.show).post(dog.new).put(dog.update);
});
Is there a clean way in modern Express without any further modules (like express-namespace)? I could go for separate routers for each model and assigning them with app.use('/cats', catRouter). However, what if I have more than one hierarchy level like '/tools/hammers/:id'? I would then have routers within routers within routers, which seems like overkill to me.
I would then have routers within routers within routers, which seems like overkill to me.
Perhaps, but that is the built-in method of prefixing -- to app.use() a Router().
var cats = express.Router();
app.use('/cats', cats);
cats.route('/').get(cat.index);
cats.route('/:id').get(cat.show).post(cat.new).put(cat.update);
// ...
And, to have one Router .use() another to define multiple depths:
var tools = express.Router();
app.use('/tools', tools);
var hammers = express.Router();
tools.use('/hammers', hammers);
// effectively: '/tools/hammers/:id'
hammers.route('/:id').get(...);
Though, to be closer to your 2nd snippet, you can define a custom method:
var express = require('express');
express.application.prefix = express.Router.prefix = function (path, configure) {
var router = express.Router();
this.use(path, router);
configure(router);
return router;
};
var app = express();
app.prefix('/cats', function (cats) {
cats.route('/').get(cat.index);
cats.route('/:id').get(cat.show).post(cat.new).put(cat.update);
});
app.prefix('/dogs', ...);
app.prefix('/tools', function (tools) {
tools.prefix('/hammers', function (hammers) {
hammers.route('/:id').get(...);
});
});
Check out the new Router in Express 4. It sounds exactly what you're looking for.
I don't know how I'd term this maybe 'static call to a koa router'? Does that seem like the right wordage here for what I'm really trying to accomplish if you were to talk about it technically?
Anyway, I'm using koa-router and I'm coding unit tests (not integration tests). So I do not want to invoke .listen() on my koa app because of that reason...it would create an http server which now makes my test an integration tests.
Instead in my test I simply want to make a straight call to the app object instance and call a route and be able to return no results and check that I returned no results in the response.
How can you do that? I can't find an example and I've tried all sorts of pseudo code attemps agains the koa app object.
If you want to test the function that koa-router routes to then just perform a unit test on that function and leave the routing out of it.
To me it sounds like you've got a file such as app.js and it contains all your code. What you can do is create a router.js file to put you route bindings and a services.js file where you can put your application logic.
So for example app.js might look like:
var koa = require("koa");
var app = module.exports = koa();
var router = require('./router.js');
app.use(router.unsecured.middleware());
app.listen(3000);
And router.js might look like:
var router = require("koa-router");
var service = require("./services.js");
var unsecured = module.exports.unsecured = new router();
unsecured.post('/account/signin', service.signinUser);
unsecured.post('/account/register', service.registerUser);
And services.js might look like:
module.exports.signinUser = function*(signinDetails) {
// contains your application signin logic
};
module.exports.registerUser = function*(registerDetails) {
// contains your application register logic
};
So in this manner you can individually test services.js. I don't see any value in individually testing router.js since it is so trivial. As #Dan Pantry shows you can test routing as part of an integration test using supertest.
Edit:
So this is a little experimental test I was playing around with to test that the routing is correct. I'm using mocha as the test runner and the code example I posted in my original code.
// standard library
var assert = require("assert");
// in app objects
var router = require('./router.js');
var service = require('./service.js');
describe("routing tests", function() {
it("test register routing, POST", function*(done) {
// arrange
var unsecured = router.unsecured;
var path = '/account/register';
var httpMethod = 'POST';
var expected = service.register.toString();
var actual;
// act
for (var i = 0; i < unsecured.stack.length; i++)
{
var pathMatch = unsecured.stack[i].path === path;
var methodMatch = unsecured.stack[i].methods.indexOf(httpMethod) >= 0;
if (pathMatch && methodMatch)
{
actual = unsecured.stack[i].middleware.toString();
break;
}
}
// assert
try {
assert.equal(expected, actual);
done();
} catch(err) {
done(err);
}
});
});
There is probably a neater way of doing this (and a more modular way for testing multiple paths) but as I said this is just a basic example to verify the routing is calling the correct service. What I'm doing is delving into the koa-router object to verify what path is bound to what service code depending on the HTTP method (e.g. POST, GET, etc).
If you have your routing and your services in modules this test completely avoids dealing with the main koa app. Although technically this test spans multiple units (the routing and the service code) so it would technically be an integration test but it does mean you don't go near app.listen() which is what you didn't want to call in your tests.