Background
Connect and its bigger brother Express are extremely popular general servers for Node. Connect takes an onion approach: it is all about layers. Each layer can answer a request, modify it or pass it to the next layer to be answered. This architecture makes it very intuitive and powerful. Each layer is called middleware and generally all one needs is to write a JavaScript function with this signature to create custom middleware:
1 | function logUrl(req, res, next) { |
To allow configuring the middleware, it is a convention to return the actual worker function from a function that accepts configuration options:
1 | function logMatchingUrls(pattern) { |
We can use same middleware multiple times, for example lets log all JavaScript and CSS requests while serving all files from folder public
1 | var app = connect() |
Testing middleware
Creating custom middleware is so easy, that the Connect community created more than 60 layers in addition to the 18 that are included with Connect itself. If you are thinking about creating your own middleware, here is how to make testing it as simple as writing it. This example is based on the connect-slow middleware I wrote to slow down serving some requests to ease debugging website loading and performance problems.
Here is a typical example for connect-slow - delay serving jquery.js by 5 seconds to look at how the website behaves during these 5 seconds.
1 | var slow = require('connect-slow'); |
There are 3 levels of testing one could do for this middleware:
- Small unit testing to make sure invalid arguments are handled properly.
- Medium sized testing with mock request, response objects and next function.
- End to end testing with actual connect stack running and separate live requests.
I decided to only create tests to check invalid inputs (1) and perform end to end testing (3). I skipped creating mock objects, because they introduce more complexity and would essentially look like end to end tests.
Small unit testing
I used my own testing runner gt that is pretty much compatible with QUnit syntax, runs natively on Node, and provides code coverage via istanbul integration.
Here are the small tests that make sure an error is thrown if url is not a RegExp, or delay is negative number. You can see the entire test file
1 | gt.test('url should be a regexp', function () { |
I did not want to create the medium mock tests, but I still wanted to make sure
the function returned by slow()
is a valid middleware function that expects 3 arguments.
So I added a unit test to check if the returned value is a function with arity 3
1 | gt.test('valid parameters', function () { |
End to end testing
Lets validate that the middleware actually delays answering some requests but not
the others. This is an example of asynchronous testing and some testing frameworks
are better at this than others. gt provides lots of support for async testing,
most relevant for this problem that it provides
setupOnce
and teardownOnce
methods that can create and tear down a Connect stack
before running multiple unit tests
1 | var request = q.denodeify(require('request')); |
Result
You can try running the tests yourself:
npm install connect-slow
cd node_modules/connect-slow
# install devDependencies needed for testing
npm install
npm test
The tests should pass, the code coverage is printed after the tests
You can open cover/lcov-report/index.html and see code coverage line by line
Bonus - pre-git
Remember, achieving passing tests is important initially, but is extremely
important in the future whenever any changes to the code are made. I use
my own pre-git project that installs
Node hooks to run before git commit
and git push
commands. For example,
to make sure tests pass before each commit, I add the following to package.json
"pre-commit": "npm test"
To make sure the code pushed to master does not depend on unlisted dependencies,
the pre-push
hook is more stringent
"pre-push": [
"rm -rf node_modules",
"npm install",
"npm test"
]
Using these safety mechanisms, you will keep your middleware working happily in the future.
Related: How to unit test Express server