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