Search code examples
reactjsunit-testingjestjsenzymespyon

Jest spy on component's property method


I'm trying to test if an event has been added in the init method called by componentDidMount, but that event is going to be added only if a component's attribute is set in "true" so I want to spy on the addEventHandler method and call the "toBeCalledWith('eventName')" so I have something like this:

export interface IMyComponentProps{
    flag?: boolean;
}

export class MyComponent extends Component<IMyComponentProps> {
    private myProperty: HTMLElement;

    public componentDidMount() {
        this.init();
    }

    private init() {
        if (this.props.flag) {
            this.myProperty.addEventListener("event", arg2, arg3);
        }
    }
}

Then I have my test looking like this:

test("Test 1", () => {
   const spyInit = jest.spyOn(MyComponent.prototype, "init");
   wrapper = mount(
      <MyComponent />
   );

   expect(spyInit).toBeCalled();
})

but the test above does not cover if the addEventListener is called or not so I'm trying different ways like following, without success:

const spyAddListener = jest.spyOn(MyComponent.prototype, "myProperty.addEventHandler"); 
const spyAddListener = jest.spyOn(MyComponent.instance().myProperty, "addEventHandler"); 
const spyAddListener = jest.spyOn(MyComponent.prototype.myProperty, "addEventHandler");

any suggestion?


Solution

  • You need to pass the flag props to the component. E.g.

    index.ts:

    import { Component } from 'react';
    
    export interface IMyComponentProps {
      flag?: boolean;
    }
    
    export class MyComponent extends Component<IMyComponentProps> {
      private myProperty!: HTMLElement;
    
      public componentDidMount() {
        this.init();
      }
      public render() {
        return null;
      }
    
      private init() {
        if (this.props.flag) {
          this.myProperty.addEventListener('event', () => null, false);
        }
      }
    }
    

    index.test.tsx:

    import { MyComponent } from './';
    import { mount } from 'enzyme';
    import React from 'react';
    
    describe('60714899', () => {
      it('should add event listener', () => {
        const spyInit = jest.spyOn(MyComponent.prototype as any, 'init');
        const mMyProperty = { addEventListener: jest.fn() } as any;
        MyComponent.prototype['myProperty'] = mMyProperty;
        const wrapper = mount(<MyComponent flag={true} />);
        expect(spyInit).toBeCalled();
        expect(mMyProperty.addEventListener).toBeCalledWith('event', expect.any(Function), false);
      });
    
      it('should NOT add event listener', () => {
        const spyInit = jest.spyOn(MyComponent.prototype as any, 'init');
        const mMyProperty = { addEventListener: jest.fn() } as any;
        MyComponent.prototype['myProperty'] = mMyProperty;
        const wrapper = mount(<MyComponent flag={false} />);
        expect(spyInit).toBeCalled();
        expect(mMyProperty.addEventListener).not.toBeCalled();
      });
    });
    

    unit test results with 100% coverage:

     PASS  stackoverflow/60714899/index.test.tsx
      60714899
        ✓ should add event listener (42ms)
        ✓ should NOT add event listener (2ms)
    
    -----------|---------|----------|---------|---------|-------------------
    File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    -----------|---------|----------|---------|---------|-------------------
    All files  |   92.31 |      100 |      80 |     100 |                   
     index.tsx |   92.31 |      100 |      80 |     100 |                   
    -----------|---------|----------|---------|---------|-------------------
    Test Suites: 1 passed, 1 total
    Tests:       2 passed, 2 total
    Snapshots:   0 total
    Time:        4.77s, estimated 10s
    

    source code: https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/60714899