In unit testing, the idea is (I think, correct me if I'm wrong) to unit test a module, then integration test these modules together, then integration test the respective "regions" of modules you create together, and so on until you have tested your entire codebase. Usually, you "pass into" each test just the module you want to test, as an object or set of objects. However, sometimes this is hard and you need to "pass in" more than that module, occasionally up to appreciable chunks of the codebase. Is this the right thing to do?
On one hand, passing in a "unit", in this case a module, is sort of the definition of unit testing. You would then integration test the modules together. Also, you would only include as much of the code as you need to and no more. Including larger parts of the codebase may not make the test much slower, but, then again, they may. On the other hand, sometimes it is hard or impossible to include only that module needed for the test, perhaps because it includes third-party code or libraries that are hard to separate, maybe because they are proprietary. You could say these libraries should be modular themselves, but they probably are, just not to the average (business-)consumer.
In my case, I am trying to test routes in an Express.js
application. I am using the new (as of version 4.x) sub-object of the express
module object called require('express').Router
. This object provides a module to do just routing. However, as it does not do much else, it is hard to test it, even for its routing functions. I end up having to pass in my entire express
object, which includes several routing sub-applications as middleware, as well as other middleware dealing with sessions, body parsing, etc., rather than just each single routing object.
Here is part of my express.router
application:
var express = require('express');
var vhosts = express.Router();
var owners = express.Router();
var vhost = require('vhost');
owners.route('/*').get(function(req, res, next) {
var owner = req.subdomains[0];
res.redirect("http://konneka.org:3000/owners/" + owner + req.path);
next();
});
module.exports.owners = owners;
vhosts.use(vhost('*.konneka.org', owners));
module.exports.all = vhosts;
As you can see, all I am really doing here is redirecting from a subdomain to a subfolder of the main domain. Then, I wrap it in a virtual hosting object, then wrap all the vhost
objects in a single express.Router
app. I send each individual routing object, as well as all of them wrapped up as a package, off to the main page as middleware.
If I pass my whole express
application to the unit tests, they have a chance at passing (Buster.js
framework). However, if I only pass single routing objects, the tests throw an error. Here is one of my test cases, with the routing object passed in. For the passing test, substitute an express
app.
// require Buster.js so our tests can run
var buster = require('buster');// expose Buster's sub-methods so we don't have to specify the entire path each time
// expose Buster's assertion methods so we don't have to specify the entire path each time
var assert = buster.referee.assert;
var refute = buster.referee.refute;
// require supertest so our tests can test HTTP easier
request = require('supertest');
buster.testCase('vhosts', {
setUp: function() {
this.vhostServer = require('../../../backend/vhost').owners;
},
// test what happens when navigating to the '*.konneka.org' subdomain
'owners submodule': {
// test that, when we visit the owner's route ([owner].konneka.org), we are redirected to
// the correct domain, 'konneka.org'
'owners route domain': function(done) {
request(this.vhostServer)
.get('/')
.set('Host', 'owners.konneka.org')
.end(function(err, res) {
assert.match(res.header.location, /(http(s)?\:)?\/\/konneka.org/);
done();
});
}
}
});
I get the error:
$ node test/buster/backend/vhosts
Error: vhosts owners subdomain domain
TypeError: Cannot call method 'apply' of undefined
TypeError: Cannot call method 'apply' of undefined
at c:\repos\konneka\node_modules\express\lib\router\index.js:532:15
at next (c:\repos\konneka\node_modules\express\lib\router\index.js:179:14)
at Layer.handle [as handle_request] (c:\repos\konneka\node_modules\express\lib\router\layer.js:78:5)
at c:\repos\konneka\node_modules\express\lib\router\index.js:227:24
at Function.proto.process_params (c:\repos\konneka\node_modules\express\lib\router\index.js:305:12)
at c:\repos\konneka\node_modules\express\lib\router\index.js:221:12
at Function.match_layer (c:\repos\konneka\node_modules\express\lib\router\index.js:288:3)
at next (c:\repos\konneka\node_modules\express\lib\router\index.js:182:10)
at Function.proto.handle (c:\repos\konneka\node_modules\express\lib\router\index.js:158:3)
at Server.router (c:\repos\konneka\node_modules\express\lib\router\index.js:27:12)
1 test, 0 assertions, 1 runtime ... 1 error
Uncaught exception in MINGW32:/c/repos/konneka v0.10.32, win32 x64:
TypeError: Cannot read property 'header' of undefined
TypeError: Cannot read property 'header' of undefined
at c:\repos\konneka\test\buster\backend\vhosts.js:29:22
at c:\repos\konneka\node_modules\supertest\lib\test.js:126:21
at Test.Request.callback (c:\repos\konneka\node_modules\supertest\node_modules\superagent\lib\node\index.js:728:30)
at ClientRequest.<anonymous> (c:\repos\konneka\node_modules\supertest\node_modules\superagent\lib\node\index.js:693:10)
at ClientRequest.emit (events.js:95:17)
at Socket.socketOnEnd [as onend] (http.js:1572:9)
at Socket.g (events.js:180:16)
at Socket.emit (events.js:117:20)
at _stream_readable.js:943:16
at process._tickCallback (node.js:419:13)
Running 1 tests in 1 runtime ... 100% done
double callback!
So, there may be a solution to my problem, a way to only test the routing sub-application and not the full express
app. However, in a larger context: In cases where it is hard or impossible to pass in only the module or part of a module you need, is it OK to pass in the entire application or a big part of it? Is this still called unit testing, or is it something else?