Dependency Injection is a really useful tool, it allows you to control what objects are created during runtime. This allows for great flexibility when writing tests.

The idea is that you inject an instance of any given class when needed, instead of just instantiating one. This allows you to manipulate how the object is injected.

  var controller = inject(Controller);

A common and use case is to swap out real objects with mock objects when writing tests. For example, if the user wanted to ensure that a given UI component would display data correctly, the user would write a test. But doing real Network Requests would be overkill and difficult to setup & control. So the user would create mock network requests instead.

Given this very simplistic network controller, that takes a url to some json feed and returns the parsed object.

/**
 * @constructor
 */
function NetworkController () {
   ...
}

/**
 * @param {string} url
 *         The url to query.
 * @return {Object}
 *         The returned data.
 */
NetworkController.prototype.get = function (url) {
   ... // do some non-async xhr here.
   return data;
}

And a ui component that uses a instance of NetworkController to query for json data.

/**
 * @constructor
 */
function UserLoginComponent () {
   ...
}

/**
 * Update the UI.
 */
UserLoginComponent.prototype.updateUI = function () {
  var controller = inject(NetworkController);
  var data = controller.get("/user/loggedInStatus.json");
  if(data.isLoggedIn) {
    this.setUserName(data.UserName);
  } else {
    this.setUserName(null);
  }
}

Now you could create a mocked Network controller that mimics the NetworkController and allows the user to set what data is returned, without doing any real network requests.

/**
 * @constructor
 */
function MockedNetworkController () {
  this.mockedData = {};
}

/**
 * @param {Object} data
 *        The mock data.
 */
MockedNetworkController.prototype.setMockedData = function (data) {
  this.mockedData = data;
}

/**
 * @param {string} url
 *         The url to query.
 * @return {Object}
 *         The returned data.
 */
MockedNetworkController.prototype.get = function (url) {
   return this.mockedData;
}

Now you could create a test that swaps out the Network Controller with a mocked Network Controller. It does this by calling

  inject.overide(NetworkController, MockController);

A test using this system would look something like this:

  setup(function () {
     // here we tell the inject function that whenever anyone asks
     // for a NetworkController, they should get a MockController instead.
     inject.overide(NetworkController, MockController);
  })

  test("Testing my the user component.", function () {
    var userComponent = new UserLoginComponent();
    var element = userComponent.getElement()


    var mockController = inject(NetworkController);
    mockController.setMockedData({
      isLoggedIn: false
    })
    userComponent.updateUI();

    assertEquals(element.innerHTML, "Please log in.");

    mockController.setMockedData({
          isLoggedIn: true,
          userName: "Stefan"
        })
    userComponent.updateUI();
    assertEquals(element.innerHTML, "Welcome, Stefan");
  });

  teardown(function () {
    // here we clear all the injections
     inject.clear();
  });