2

i have a script that defines 2 classes, and 1 gets instantiated from within the constructor of the other. how can i mock the nested constructor, so i can test the parent constructor?

export default class Foo { // i want to test this constructor... constructor() { new Bar } } export class Bar { // but mock this constructor constructor() {} } 

additionally, i am trying to spy on the Bar constructor, to assert that it has been called

i have tried several different approaches, but haven't been able to get the results i am looking for. i am new to the jest mocking library

2
  • Are these classes both in the same module? I read this recently, which suggests that you should separate them into different module files if you want to mock one and not the other. Commented Oct 27, 2020 at 6:11
  • A constructor is the same as a class that it represents. Literally, Bar.prototype.constructor === Bar. So that it's a constructor doesn't offer any opportunities regarding testing. You cannot mock a variable that is used in the same module where it was defined, end of the story. That Foo constructor contains side effect and ignores Bar instance is potentially an antipattern and should be addressed in the first place. If there were this.bar = new Bar you could assert it. Commented Oct 27, 2020 at 7:58

1 Answer 1

6

Need to make a little modification to the module export statement. Then, we can use jest.spyOn(object, methodName) method to mock implementation for Bar class. Take a look at the code after compiling. We create the mocked Bar in the module export object and we use it in the Foo class. It has the same reference as the mocked one.

Recommend way:

  1. Dependency Injection
  2. Each file contains ONLY ONE class. So that we can use jest.mock or jest.doMock method to mock the class without modifying the module export statement.

E.g.

index.ts:

export default class Foo { constructor() { new exports.Bar(); } } class Bar { constructor() { console.log('real Bar constructor implmentation'); } } exports.Bar = Bar; 

index.test.ts:

import * as mod from './'; console.log(mod); describe('64549093', () => { it('should pass', () => { const BarConstructorMock = jest.spyOn(mod as any, 'Bar').mockImplementationOnce(() => { console.log('fake Bar constructor implmentation'); }); new mod.default(); expect(BarConstructorMock).toBeCalled(); }); }); 

unit test result:

 PASS src/stackoverflow/64549093/index.test.ts (9.905s) 64549093 ✓ should pass (5ms) console.log src/stackoverflow/64549093/index.test.ts:3 { default: [Function: Foo], Bar: [Function: Bar] } console.log src/stackoverflow/64549093/index.test.ts:8 fake Bar constructor implmentation Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 11.751s, estimated 12s 

About the configuration of jestjs, TypeScript, see example: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/64549093

Sign up to request clarification or add additional context in comments.

10 Comments

this is incredibly helpful! not only does it help resolve my issue, but it helps me better understand jest. tyvm for taking the time to provide such clear & detailed info! :)
Original code contains only ESM, not CJS. It's a known workaround for this problem to use exports object but that ESM and CJS are mixed in the same module is a hack that smells bad.
@EstusFlask is this a reference to slideshowp2's 2nd bullet 'Each file contains ONLY ONE class'?
actually i found docs on the import syntax, but not using mod as any with jest.spyOn. using jest.spyOn(mod, Bar) seems to work as well, but what does adding as any do? is this just a typescript thing? or does it have meaning in js?
@brewster That's a reference to new exports.Bar() in the example. Having one module per class is the only reasonable solution here, not what the example shows. Yes, as any is TS thing. And it's unsafe to rely on jest.spyOn(mod, Bar) because modules are read-only by specs and they can really be read-only in Jest depending on the setup.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.