Skip to content

Instantly share code, notes, and snippets.

@fiveisprime
Last active December 23, 2015 13:39
Show Gist options
  • Save fiveisprime/6643459 to your computer and use it in GitHub Desktop.
Save fiveisprime/6643459 to your computer and use it in GitHub Desktop.

Designing for Testability

An overview of design practices for building modules/applications to best facilitate unit testing.

Goal

The primary goal of the unit test is to be able to exercise as much functionality as possible within a certain module (unit). The best way to achieve this is to completely separate functionality into units (or modules) with any external dependencies being injected at initialization.

Example:

A user controller should contain only user validation. User objects need to be persisted to a data store; however, database functionality is a dependency that should be injected at initialization time.

Why:

The user controller unit tests must be limited to only the functionality of the user controller. Once a database call is made, the process is out of scope of the unit test (these pieces are either tested in the database unit test or not at all in the case of external libraries). Injecting the database controller as a dependency allows the database controller to be mocked prior to being injected, correctly separating the concerns of both the user controller logic and the user controller tests.

Express Application

Suggested structure:

|-- server.js (main script)
|-- controllers
|   |-- index.js (bootstrap script for controllers)
|   |-- user_controller.js
|   `-- project_controller.js
|-- models (database functionality - could use a better name)
|   |-- index.js (bootstrap script for models)
|   |-- user_model.js
|   `-- project_model.js
`-- routes
    |-- index.js (bootstrap script for routes)
    |-- user_routes.js
    `-- project_routes.js

Server.js initializes the express application and attaches all of the pieces - it depends on routes which depend on controllers, which depend on models:

var express     = require('express')
  , app         = express()
  , controllers = require('./controllers')
  , models      = require('./models')
  , routes      = require('./routes');

//
// Initialize express middleware...
//

models.init();
controllers.init(models);
routes.init(app, controllers);

app.listen(process.env.PORT || 8888);

The bootstrap scripts simply initialize each of the modules with the dependencies that are passed into the init function which means that dependencies are controlled by the script that initializes the module.. in other words, a unit test can now pass mocks for the dependencies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment