-
Notifications
You must be signed in to change notification settings - Fork 57
Testing
Express Train gives you a DI system, so how do you take advantage of that to write nice, DRY tests? I'll walk through some examples. In these examples I am using mocha and chai expectations, since they are my preference. Any testing framework will work fine, though.
So, each of your express train modules is a function that takes its injectables as arguments. The simplest approach is just to manually inject mock dependencies before or in each test. For example, say we are testing a controller:
// UsersCtrl: takes Users model as injected dependency
module.exports = function(Users){
controller = {};
return {
index: function(req, res, next){
Users.getAll(function(err, result){
if(err) return next(err)
return result
})
}
}
In our test file we can then write...
UsersCtrl = require('../controllers/UsersCtrl')
describe('UsersCtrl', function(){
usersCtrl = null
mockUsers = null
beforeEach(function(){
mockUsers = {getAll: function(){ /* doing mock stuff...*/ }}
usersCtrl = UsersCtrl(mockUsers)
})
//do some testing!
})
The previous example works fine, but it can be a bit brittle. Any time we re-arrange the arguments, add, or remove dependencies in the module, our tests will break. To make them more robust, we can use nject to create and resolve a new dependency tree for each test. This mimics what is actually happening when we boot up an express train application, and means that we don't have to maintain a specific order of arguments when we instantiate the modules in our tests.
Assuming the same UsersCtrl module as before, we would now have:
Tree = require('nject').Tree
UsersCtrl = require('../controllers/UsersCtrl')
describe('UsersCtrl', function(){
usersCtrl = null
mockUsers = null
beforeEach(function(done){
tree = new Tree()
mockUsers = {getAll: function(){ /* doing mock stuff...*/ }}
tree.constant('Users', mockUsers)
tree.register('usersCtrl', UsersCtrl)
tree.resolve(function(err, resolved){
if(err) return done(err)
usersCtrl = resolved.usersCtrl
})
})
//do some testing!
})
If you are not familiar with nject, this might not be super familiar. It is worth taking a look at the docs here, since this relates directly to how express train registers and dynamically resolves its dependency tree. Obviously this example is a little bit contrived, but let's walk through what's happening here:
- Before each test we set up a new dependency tree.
- We create our mock object as before. It is registered to the tree as a constant, since it is not a function with its own dependencies that need to be resolved. Note that the registration key
Users
matches the injected variable name in our UsersCtrl module. - We register our module with the tree, then resolve it. The mocks will be injected at resolution.
While this does not get us much in our simple example, you can imagine that it becomes more useful as a module has many dependencies.