Skip to content

Instantly share code, notes, and snippets.

@Haosvit
Last active August 19, 2021 13:20
Show Gist options
  • Save Haosvit/2671528fec3b82e99d8607d025f601d4 to your computer and use it in GitHub Desktop.
Save Haosvit/2671528fec3b82e99d8607d025f601d4 to your computer and use it in GitHub Desktop.
Jest technique
  • use Jest Runner extension: [link]

Debug Jest:

ShallowWrapper {
...
Symbol(enzyme.__node__) {
  props: { // use this to search for item with property:value pair. Ex: wrapper.find('MenuItem[title="View"]')
    children: [{}],
    ...others
  },
  type: {
    displayName: string, // this should be the thing to find.
  }
}
...
}

Test with 'Mount'

  let wrapper = mount(<TextField type='text' required />);
  const input = wrapper.find('input');
  input.simulate('blur', { target: { value: '' } });
  console.log(wrapper.debug());
  const icon = wrapper.find('FontAwesomeIcon');
  expect(icon).toHaveLength(1);

Shallow

NEVER REUSE 'find' result. State will not be updated.

Jest mock

Mock module

Mock children react component of Component-Under-Test (CUT): Find import path of to-be-mocked children, mock in test like below:

Case 1:

ChildComponent.tsx:
....
export const A;
export const B;
jest.mock('path/to/childComponent', () => {
    return {
        A: true, // return the real implementation of 'A'
        B: 'MockB', // return mock of 'B', will be '<MockB />' when shallow
    }
});

Case 2: ChildComponent.tsx:

... export const A;
export default B;
jest.mock('path/to/childComponent', () => {
    return {
        __esModule: true, // MUST SPECIFY
        A: true, // return the real implementation of 'A'
        default: () => <div> </div>
    }
});

Mock React useRef

const refObj: React.MutableRefObject<Record<string, unknown> = {
    current: {},
};
const mockedUseRef = jest.fn(obj => {
    refObj.current = obj;
    return refObj;
});

Mock i18next

jest.mock('../../../config/i18next', () => {
    return {
        __esModule: true,
        default: {
            exists: () => true,
            t: (key) => `t(${key})`,
        }
    }
}); 

Mock api call

import axios from "axios";

const mockApi = new MockAdapter(axios);

it('..', () => {
 mockApi.onAny().reply(500);
 
 // assertion
 mockAPi.reset()
});

Spy on api:

  const apiSpy = spyOn(apiService, "get");
  expect (apiSpy).toHaveBeenCalledWith([..]);

! mockAPI and spyOn does not work in the same test case

Test async methods

  1. mock:
    const asyncMethod = jest.fn().mockResolvedValue('abc');
  2. useFakeTimers:
jest.useFakeTimers();
component.props().onSearch(value);
jest.runAllTimers();
expect(asyncMethod).toBeCalledWith('abc');
jest.mock('moment', () => {
  const moment = jest.requireActual('moment');

  return {default: moment };
});

Expect throw with async

it('returns error', async () => {
    const apiCall = async () =>
        await get(endpoint, {
            param: 'yee',
        });

    await expect(apiCall()).rejects.toThrow();
});

Jest saga

Using stdChannel

import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { runSaga as reduxRunSaga, stdChannel } from 'redux-saga';

const mockApi = new MockAdapter(axios);

    it('waits and dispatches', async () => {
        mockApi.onPost().reply(200, mock1);
        mockApi.onGet().reply(200, mock2);

        const channel = stdChannel();
        const actions = [];
        const saga = reduxRunSaga(
            {
                channel,
                dispatch: action => {
                    actions.push(action);
                },
                getState: () => mockState,
            },
            sagaUnderTest,
        );
        channel.put(triggerActionAsync.success(mock1));
        await saga.toPromise();

        expect(actions).toContainEqual(a);
        expect(actions).toContainEqual(b);
        expect(actions).toContainEqual(c);
    });
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment