2.4 Stub ES6 Class “extend”

Historically, JavaScript has used protypal inheritance and constructor functions to deliver application code in an object-oriented fashion.

ES6 has introduced the JavaScript class keyword. To some extent JavaScript classes are syntactic sugar over the JavaScript prototype.

This poses a new problem for testing, how does one test a JavaScript class that extends another. This is known as inhertances in object-oriented programming circles.

The previous section has shown how one might test a global class’ invocation, this section will look at how to do the same for inheritance.

Stub ES6 class inheritence (class Foo “extends” Bar)

This section goes through some patterns that can be used to unit test ES6 classes.

The examples will use Jest module auto-mocking but should be portable to other module mocking libraries (eg. Proxyquire) with some modifications.

We’ll be testing the following model (which is an ES6 class):

const { Model } = require('sequelize');

class MyModel extends Model {
  static init() {
    return (
      // Config
      super
        .init()
    );
  }

  isAvailable(date) {
    if (!Array.isArray(this.meetings)) {
      throw new TypeError('meetings should be eager-loaded');
    }

    return !this.meetings.some(
      ({ startDate, endDate }) => startDate < date && endDate > date
    );
  }
}

module.exports = MyModel;

At the module level, we want to:

  • mock/stub out Sequelize (and the Model base class)
  • import the model

In the test:

  • Instantiate the model that we’ve defined (without crashing)
  • Set some properties on that instance
  • Run some methods
  • Assert on the output
jest.mock('sequelize');
const Model = require('./02.04-model');

test('It should not throw when passed a model containing an empty list of meetings', () => {
  const model = new Model();
  model.meetings = [];
  expect(model.isAvailable.bind(model, new Date(Date.now()))).not.toThrow();
});

Alternative with Object.assign

If we’re setting more than a single instance property, using Object.assign can be easier to manage:

// other tests
test('It should not throw when passed a model containing an empty list of meetings', () => {
  const model = Object.assign(new Model(), {
    meetings: []
  });
  expect(model.isAvailable.bind(model, new Date(Date.now()))).not.toThrow();
});

The tests both pass as per test output.

npx jest src/02.04-model-mock.test.js
 PASS  src/02.04-model-mock.test.js
  ✓ It should not throw when passed a model containing an empty list of meetings (25ms)
  ✓ It should not throw when passed a model containing an empty list of meetings (1ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total

The key part of both of these is the .bind(model, /* rest of params */), this is, as so many other times, not a Jest-specific trick, rather how JavaScript works. .bind sets the this context to its first parameter, and the rest of the parameters are passed as arguments to the function that’s being bound.

This section has gone through 2 approaches to testing a class that inherits from another.

The next sections will dive deeper into creating object mocks for tests.

Jump to table of contents