How to fix “Received number of calls: 0” when testing a class method?
- Main classes
class API { someMethod() { ... } } export default API;
import API from 'utils/API'; class Foo { constructor() { this.api = new API(); } helloWorld() { this.api.someMethod(); } } export default Foo;
- Failed test
describe( 'Foo', () => { it ( 'should call someMethod', () => { const foo = new Foo(); const api = new API(); jest.spyOn( api, 'someMethod' ); foo.helloWorld(); expect(api.someMethod).toHaveBeenCalledTimes(1); }); });
expect(jest.fn()).toHaveBeenCalledTimes(expected) Expected number of calls: 1 Received number of calls: 0
- Correct test file
const mockSomeMethod = jest.fn(); // Make variables that start with the word mock jest.mock( 'utils/API', () => { // Call jest.mock with module factory parameter return jest.fn().mockImplementation(() => { return { someMethod: mockSomeMethod }; }); }); describe( 'Foo', () => { it ( 'should call greetWorld', () => { const foo = new Foo(); const api = new API(); foo.helloWorld(); expect(api.greetWorld).toHaveBeenCalledTimes(1); }); });
Ref: inner-functions-with-jest
How to mock a class to return a function instead of an object?
class API { someMethod() { ... } } export default API;
- If a class is mistakenly mocked as an “object” as opposed to a function, an object cannot be instantiated with a “new” operator.
This mock is incorrect because it returns an object:
import API from 'utils/API'; const mockSomeMethod = jest.fn() jest.mock('utils/API', () => { // Returns an object return { someMethod: mockSomeMethod }; }) // This will throw the error const api = new API();
- This mock is correct:
import API from 'utils/API'; const mockSomeMethod = jest.fn() jest.mock('utils/API', () => { // Returns a function return jest.fn().mockImplementation(() => ({ someMethod: mockSomeMethod })); }) // This will not throw an error anymore const api = new API();
How to mock a class which is exported as non-default?
The error you usually get for this case is “TypeError: _myClass.MyClass is not a constructor“
export class API { someMethod() { ... } }
import API from 'utils/API'; const mockSomeMethod = jest.fn() jest.mock('utils/API', () => { return { get API() { return jest.fn().mockImplementation(() => ({ someMethod: mockSomeMethod })); } } }) const api = new API();
How to test async/ await?
export class API { async fetchMethod() { ... } errroMethod() { ... } //////////////////////////////// //////////////////////////////// async someMethod() { try { await fetchMethod() } catch(error) { errorMethod() } } }
import API from 'utils/API'; const api = new API(); const mockErrorMethod = jest.spyOn(api, 'errorMethod'); describe('API fecth works', () => { jest.spyOn(api, 'fetchMethod').mockReturnValue(Promise.resolve()); it('calls fetchMethod, errorMethod is not called', async () => { await api.someMethod; expect(api.fetchMethod).toHaveBeenCalledTimes(1); expect(mockErrorMethod).toHaveBeenCalledTimes(0); }); }) describe('API fecth is down', () => { jest.spyOn(api, 'fetchMethod').mockReturnValue(Promise.reject(error)); it('calls fetchMethod, errorMethod', async () => { await api.someMethod; expect(api.fetchMethod).toHaveBeenCalledTimes(1); expect(mockErrorMethod).toHaveBeenCalledTimes(1); }); })
How to test a component with a custom hook
// click-a-button.tsx import {useClickAButton} from "./hooks/index"; export const ClickAButton = () => { const { handleClick, total } = useClickAButton(); return <button onClick={handleClick}>{total}</button>; }
// hooks/use-click-a-button.tsx import React, {useCallback, useState} from 'react'; export const useClickAButton = () => { const [total, setTotal] = useState<number>(0); const handleClick = useCallback(() => { setTotal(total => total + 1); }, []); return { handleClick, total, }; }
// click-a-button.test.tsx import * as React from 'react'; import {act} from "react-dom/test-utils"; import {render} from "@testing-library/react"; import {useClickAButton} from './hooks/index' import {ClickAButton} from "./index"; jest.mock('./hooks') // This is important const hooks = { useClickAButton } const mockUseClickAButton = jest.spyOn(hooks, 'useClickAButton'); const mockHandleClick = jest.fn(); mockUseClickAButton.mockReturnValue({ handleClick: mockHandleClick, total: 5, }); test('it runs with a mocked customHook',() => { const component = render(<ClickAButton />); expect(component.container).toHaveTextContent('5'); act(() => { component.container.click(); }); expect(mockHandleClick).toHaveBeenCalled(); })
How to mock a hook?
// navigation.ts import { createNavigationContainerRef } from '@react-navigation/native'; export const navigationRef = createNavigationContainerRef(); ...
Error when running the test file
TypeError: (0 , _native.createNavigationContainerRef) is not a function
// Test file const mockNavigation = jest.fn(); jest.mock('@react-navigation/native', () => ({ ...jest.requireActual('@react-navigation/native'), createNavigationContainerRef: () => { return mockNavigation; }, useNavigation: () => { return mockNavigation; } }))
How to test a button press and fire an event?
// MyComponent.tsx ... return <MyButton // Setup testID testID={constants.testId.myButton} // onPress action onPress={() => navigateToSomewhere(link)} /> ...
// Test file // Mock action const mockAction = jest.spyOn(anObject, 'navigateToSomewhere').mockImplementation(); // Get button view by id const { getByTestId } = render(<MyComponent />); const button = getByTestId(constants.testId.myButton); await waitFor(() => expect(button).toBeTruthy()); // Perform press action fireEvent.press(button); // Test result expect(mockAction).toBeCalled();
How to test an API request using Axios?
// index.tsx export const getUserProfile = async () => { try { const response = await axios.get(userProfileUrl) return response.data; } catch (error) { throw error; } }
import axios from 'axios'; // Mock axios jest.mock('axios'); const mockAxios = axios as jest.Mocked<typeof axios>; describe('fetch user profile sucessfully', () => { it('should return user profile', async () => { // Mock return value mockAxios.get.mockResolvedValue(mockData); // Perform axios request await getUserProfile(); // Result expect(axios.get).toHaveBeenCalledWith(`${userProfileUrl}`) }) })
How to test if an element is displayed?
const { getByTestId, toJSON } = render(<MyComponent />); // Snapshot expect(toJSON()).toMatchSnapshot(); // Get element by Id const button = getByTestId(testId.button.login); expect(button).toBeTruthy();
Leave a Reply