javascriptjestjsconstructorspyon

How to jest.spyOn an instance method called in the constructor


Simplified problem case:

export class MyClass {

    constructor() {
        this.myMethod();
    }

    myMethod() {
        console.log(42);
    }

}

Testing the constructor:

describe('CLASS: MyClass', () => {
    let sut: MyClass;

    beforeEach(() => {
        jest.clearAllMocks();
        sut = new MyClass();
    });

    describe('CONSTRUCTOR', () => {
    
        test('should construct correctly and call myMethod', () => {
            const spy = jest.spyOn(sut, 'myMethod').mockImplementationOnce(jest.fn());
    
            expect(sut).toBeTruthy();
            expect(spy).toHaveBeenCalled();
        });    
    });
});

Of course this doesn't work, as the spy is initiated after sut is constructed, so it can't register the call.

Neither is it possible to initiate the spy before sut, as it can't spy on something that doesn't exist yet.

Nor did I have success trying to spy on MyClass.prototype.

Sure, I could spy on the implementation details of myMethod (basically jest.spyOn(console, 'log'). But that defies the separation of units for testing.

It's probably trivial, but what am I missing, how to get this very simple test to work?


Solution

  • You should spy on MyClass instead of the instantiated class, sut.

    You can still spy on methods without mocking the implementation but it's probably better here to avoid the code in it getting executed and logging 42 in the console.

    describe('CLASS: MyClass', () => {
      let sut: MyClass
      let spy: jest.SpyInstance = jest.spyOn(MyClass.prototype, 'myMethod')
    
      beforeEach(() => {
        jest.clearAllMocks()
        spy.mockImplementationOnce(jest.fn())
        sut = new MyClass()
      })
    
      describe('CONSTRUCTOR', () => {
        test('should construct correctly and call myMethod', () => {
          expect(sut).toBeTruthy()
          expect(spy).toHaveBeenCalled()
          expect(spy).toHaveBeenCalledTimes(1)
        })
      })
    })