A Jar of Clay
A Jar of Clay

Reputation: 6298

Test the method of a subclass

I am trying to test that method X of class A calls imported function Y. Class A is a subclass of class B, which should be mocked out.

Class A looks like this:

const B = require('./B');
const { Y } = require('../util');
class A extends B {
  constructor() {
    super('/A');
    this.setCors('');
    this.app.post('', this.X.bind(this));
  }

  X(req, res) {
    Y();
  }
}

module.exports = A;

The attempt at testing (following Jest Official Docs):

const A = require('../path/to/A');
const B = require('../path/to/B');
jest.mock('../path/to/B', () => {
  return jest.fn().mockImplementation(() => {
    return { setCors: jest.fn(), app: { post: jest.fn() } };
  });
});

test('method X calls function Y', () => {
  (new A()).X();
  expect(Y).toBeCalled();
});

This gives the error TypeError: Cannot read property 'bind' of undefined about the constructor of A.

Perhaps it is necessary to mock out just the constructor, but I'm not sure how to do that.

Upvotes: 0

Views: 427

Answers (1)

Lin Du
Lin Du

Reputation: 102207

Here is the solution:

Folder structure:

.
├── A.spec.ts
├── A.ts
├── B.ts
└── util.ts

A.ts:

import B from './B';
import { Y } from './util';

export default class A extends B {
  constructor() {
    super('/A');
    this.setCors('');
    this.app.post('', this.X.bind(this));
  }

  public X(req, res) {
    Y();
  }
}

B.ts:

export default class B {
  public app = {
    post(route: string, controller: () => any) {
      //
    }
  };
  private name: string = '';
  private url: string = '';
  constructor(name: string) {
    this.name = name;
  }

  protected setCors(url: string) {
    this.url = url;
  }
}

util.ts:

export function Y() {
  console.log('I am Y');
}

A.spec.ts:

import A from './A';
import { Y } from './util';

jest.mock('./util.ts', () => {
  return {
    Y: jest.fn()
  };
});

const a = new A();

describe('A', () => {
  it('should execute function Y correctly', () => {
    const mockedReq = {};
    const mockedRes = {};
    a.X(mockedReq, mockedRes);
    expect(Y).toBeCalledTimes(1);
  });
});

Unit test result with 100% coverage report:

PASS  src/stackoverflow/52075711/A.spec.ts (5.747s)
  A
    ✓ should execute function Y correctly (8ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |      100 |      100 |                   |
 A.ts     |      100 |      100 |      100 |      100 |                   |
 B.ts     |      100 |      100 |      100 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        9.623s, estimated 14s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/52075711

Upvotes: 1

Related Questions