In the previous section, we tested each of our Backbone entities against a mock server, using Sinon to fake responses to each collection’s HTTP requests. Now it’s time to create the mirror image of those tests, testing that our server provides the correct response to a set of fake requests.
Before we start writing tests, though, we should refactor our server to make it test-friendly. As we wrote it originally, it would define an Express instance (called app) and then immediately start listening on a port. For testing, we don’t want to actually open a TCP port. It’s much easier to simulate our requests. So let’s separate the code that defines app from the code that tells app to start listening:
| # Read environment configuration |
| env = process.env.NODE_ENV or 'development' |
| |
| # In development mode, enable source map support |
| if env is 'development' |
| require('source-map-support').install() |
| |
| # Create an Express server instance |
| express = require 'express' |
| app = express() |
| |
| app.use(express.static("#{__dirname}/public")) |
| |
| Datastore = require('nedb') |
| db = {} |
| ['boards', 'columns', 'cards'].forEach (collectionKey) => |
| db[collectionKey] = new Datastore |
| filename: "#{__dirname}/#{collectionKey}.db" |
| autoload: true |
| |
| db[collectionKey].ensureIndex {fieldName: 'id', unique: true} |
| return |
| |
| # Set the initial board state if none already exists |
| db.boards.insert({ |
| id: 1 |
| name: 'New Board' |
| }) |
| |
| bodyParser = require('body-parser') |
| app.use(bodyParser.json()) |
| |
| ['boards', 'columns', 'cards'].forEach (collectionKey) => |
| |
| # Endpoint to fetch the entire collection |
| app.get "/#{collectionKey}", (req, res) => |
| db[collectionKey].find {}, (err, collection) => |
| throw err if err |
| res.send(collection) |
| return |
| |
| # Endpoint to add a new object to the collection (assigns id) |
| app.post "/#{collectionKey}", (req, res) => |
| object = req.body |
| db[collectionKey].count {}, (err, count) => |
| throw err if err |
| object.id = count + 1 |
| db[collectionKey].insert object, (err) => |
| throw err if err |
| res.send(object) |
| return |
| |
| # Endpoint to update an existing object in the collection |
| app.put "/#{collectionKey}/:id", (req, res) => |
| query = {id: +req.params.id} |
| object = req.body |
| options = {} |
| db[collectionKey].update query, object, options, (err) => |
| throw err if err |
| res.send(object) |
| return |
| |
| # Export the server instance |
| module.exports = app |
Because this module just defines and exports app, it’s now dual-purpose. We can require it and run assertions against it, or we can require it and call the listen method to start the server.
Now to write some Node tests. In the browser, we used Sinon to simulate Ajax requests. Here, we’ll use Supertest[59] to simulate requests to our Node server. Let’s start by installing Supertest as a dev dependency:
| $ npm install --save-dev supertest |
Now let’s create a new folder for our new tests:
| $ mkdir tests/src/node_unit |
And a Gruntfile section, just like the one for our UI tests but with runType: ’client’ (meaning “Node client”) instead of runType: ’runner’:
| node_unit: |
| options: |
| runType: 'client' |
| config: 'tests/compiled/node_unit/intern' |
| reporters: ['console'] |
For Node tests, we don’t need anything like Selenium, so our Intern config will be much simpler:
| define |
| excludeInstrumentation: /^(?:tests|node_modules)// |
| suites: [ |
| 'tests/compiled/node_unit/cardEndpoint' |
| ] |
(Notice that we’ve excluded node_modules from instrumentation here, since we aren’t worried about how well our tests cover our third-party libraries.)
Our dependencies will be simpler as well. All we need is our basic testing libraries and the module that defines our Express instance:
| define [ |
| 'intern!object' |
| 'intern/chai!assert' |
| 'intern/dojo/node!supertest' |
| 'intern/dojo/node!../../../lib/app' |
| ], (registerSuite, assert, supertest, server) -> |
Now for the test itself. At a minimum, we should make sure that the /cards endpoint yields a 200 response, while a request to a nonexistent endpoint yields a 404:
| registerSuite |
| name: 'card endpoint' |
| |
| 'request for cards yields a 200 response': -> |
| supertest(server).get('/cards') |
| .expect(200) |
| .end (err, res) => |
| throw err if err |
| |
| 'request for bad URL yields a 404 response': -> |
| supertest(server).get('/adsfsfd') |
| .expect(404) |
| .end (err, res) => |
| throw err if err |
We should, of course, write analogous tests for every endpoint our server provides, but this is a good starting point. Now to build our Node server and run our final batch of tests:
| $ grunt build |
| $ grunt intern:node_unit |
| >> PASS: main - card endpoint - request for cards yields a 200 response (8ms) |
| >> PASS: main - card endpoint - request for bad URL yields a 404 response (1ms) |
| >> 0/2 tests failed |
| >> 0/2 tests failed |
3.144.47.218