|

Manual Dependency Injection in JavaScript

Dependency injection (DI) is a pattern used to write modular, loosely coupled components. Specifically, you inject specific dependencies into a module at run-time rather then loading them in a hard coupled fashion at compile time.

Before we delve into ways to do dependency injection let’s take a look at a simple example that could benefit from DI:

var mongo = require("mongo-col")("DI example")

var store = module.exports = {
    set: set
    , get: get
}

function get(key, callback) {
    mongo.findOne({
        key: key
    }, function (err, result) {
        callback(err, result && result.value)
    })
}

function set(key, value, callback) {
    mongo.update({
        key: key
    }, {
        $set: {
            value: value
        }
    }, {
        upsert: true
        , safe: true
    }, callback)
}

Above is an example implementation of a little store library that uses mongoDB for persistance of key value pairs. It uses mongo-col to reduce mongoDB boilerplate code.

To illustrate why DI is useful let’s try to test the above code.

// uses real database! naughty
var store = require("./index")
    , assert = require("assert")

store.set("foo", "bar", function (err) {
    store.get("foo", function (err, value) {
        assert.equal(value, "bar")
        console.log("DONE")
        // How do we close the database?
    })
})

We can identify a few issues with the above code:

Now, ideally when we write a unit test we test the unit of code rather then the database. DI allows us to inject the database dependency of the store into module in such a way that we can inject a fake database when testing.

Constructor Injection

Dependency injection using constructors is simple.

module.exports = storeConstructor

function storeConstructor(mongo) {
    return {
        set: set
        , get: get
    }

    function get(key, callback) {
        mongo.findOne({
            key: key
        }, function (err, result) {
            callback(err, result && result.value)
        })
    }

    function set() {
        ...
    }
}

Rather then exporting the module, you export a function that takes the dependencies as arguments. Then when you come to test or use the module you make sure to call the constructor with the correct dependencies.

var mongo = require("../mocks/mongo")
    , store = require("./store")(mongo)
    , assert = require("assert")

store.set("foo", "bar", function (err) {
    store.get("foo", function (err, value) {
        assert.equal(value, "bar")
        console.log("DONE")
    })
})

Then in our tests we get a mock version of the mongodb instance and we pass it directly into the store constructor.

For a more complete example see DI-example.

This achieves our goal of wanting to write unit tests that do not interact with the database. There are other ways to do DI which have their own trade offs.

Safari Books Online has the content you need

Take advantage of these node.js resources in Safari Books Online:

In object-oriented programming, a central program normally controls other objects in a module, library, or framework. With dependency injection, this pattern is inverted—a reference to a service is placed directly into the object which eases testing and modularity. Spring or Google Guice use dependency injection so you can focus on your core application and let the framework handle infrastructural concerns. Dependency Injection: Design patterns using Spring and Guice explores the DI idiom in fine detail, with numerous practical examples that show you the payoffs. You’ll apply key techniques in Spring and Guice and learn important pitfalls, corner-cases, and design patterns. Readers need a working knowledge of Java but no prior experience with DI is assumed.
Node: Up and Running (Rough Cuts) introduces you to Node, the new web development framework written in JavaScript. You’ll learn hands-on how Node makes life easier for experienced JavaScript developers: not only can you work on the front end and back end in the same language, you’ll also have more flexibility in choosing how to divide application logic between client and server.
Read What Is Node? to get up to speed on Node.js with this concise overview.
Node for Front-End Developers shows you how to use this popular JavaScript framework to create simple server applications, communicate with the client, build dynamic pages, work with data, and tackle other tasks.
In Sams Teach Yourself node.js in 24 Hours, expert web developer George Ornbo guides readers through every step of creating custom server-side solutions with Node.js.

About this author

Jake Verbaten is a JavaScript evangelist and open source hacker. He’s published a ton of modules on npm and build multiple production applications using node.js. To learn more about Jake, check out his github profile.

About Safari Books Online

Safari Books Online is an online learning library that provides access to thousands of technical, engineering, business, and digital media books and training videos. Get the latest information on topics like Windows 8, Android Development, iOS Development, Cloud Computing, HTML5, and so much more – sometimes even before the book is published or on bookshelves. Learn something new today with a free subscription to Safari Books Online.
|

Comments are closed.