Testing React Native Components
In order to test whether a button click works in a React Native app using Jest, you can use the fireEvent
method from the @testing-library/react-native
library.
Debugging
Debug()
When retrieving an element fails, use screen.debug()
to inspect the rendered screen in code
screen.debug()
await new Promise(r => setTimeout(r, 2000))
screen.debug({ mapProps: ({ style, ...props }) => ({ props }) })
screen.debug({ mapProps: ({ ...props }) => ({}) })
---
<RNCSafeAreaProvider
onInsetsChange={[Function anonymous]}
style={
Array [
Object {
"flex": 1,
},
undefined,
]
}
>
<View
style={
Object {
"backgroundColor": "background",
"elevation": 1,
...
Sometimes, adding an intentional delay helps in debugging the issue.
await new Promise((r) => setTimeout(r, 2000))
Examples
You can find many good examples from Testing Library Example page.
Button
Here is an example of how you might test a button click:
import React from "react"
import { fireEvent, render } from "@testing-library/react-native"
import CommonButton from "../../src/components/CommonButton"
import wrapper from "../wrapper.helper"
describe("CommonButton", () => {
it('renders correctly', () => {
expect(true).toBe(true)
const { getByText } = render(
<CommonButton title="Test Button" onPress={() => {}} />,
{wrapper}
)
expect(getByText("Test Button")).toBeTruthy()
})
it('calls onPress when pressed', () => {
const onPress = jest.fn()
const { getByTestId } = render(
<CommonButton title="Test Button" onPress={onPress} />,
{wrapper}
)
fireEvent.press(getByTestId("common-button"))
expect(onPress).toHaveBeenCalled()
})
})
In this example, we first import the render
and fireEvent
methods from the @testing-library/react-native
library. Next, we import the component we want to test, CommonButton
in this case. Then we use the render
method to render the component in a way that we can test.
After that, we use the getByTestId
to fetch the button element from the render tree and store it in a variable named button, then we use fireEvent.press(button)
to simulate the button click. Finally, we use expect(onPress).toHaveBeenCalled()
to check if the onPress
callback was called during the simulation.
Mock and Verify
KeyboardAwareScrollView Issue
render()
doesn’t render KeyboardAwareScrollView
very well. Mock it so that it just render the children.
export const KeyboardAwareScrollView = ({ children }) => children
Verify that a mocked api is called with the expected parameters
To verify that a mocked API is called with the expected parameters in Jest, you can use expect.toHaveBeenCalledWith(arg1, arg2, ...)
method on the mock function.
describe('SignIn', () => {
const email = 'email'
const password = 'password'
apis.authSignIn = jest.fn()
it('should sign in with given email and password', () => {
render(<SignIn />, {wrapper})
fireEvent.changeText(screen.getByTestId('email'), email)
fireEvent.changeText(screen.getByTestId('password'), password)
fireEvent.press(screen.getByTestId('sign-in-button'))
expect(apis.authSignIn).toHaveBeenCalledWith({
email: email,
password: password,
})
})
})
Capture the parameters that are passed to a mocked function
You can capture the parameters that are passed to a mocked function by accessing the .mock.calls
property of the mock function.
describe('JobDetails', () => {
apis.updateJob = jest.fn()
it('should complete job on confirm', async () => {
render(<JobDetails />, {wrapper})
fireEvent.press(await waitFor(() => screen.getByTestId('complete-button')))
fireEvent.press(screen.getByTestId('alert-dialog-action-button'))
const job = apis.updateJob.mock.calls[0][0] as Job
expect(job.jobStatus).toBe('COMPLETED')
})
The module factory of jest.mock()
is not allowed to reference any out-of-scope variables
I got this error when I use an object I imported from another module to return it as mocked response. You have to change the name of the variable to start with mock
// apiMocks.ts
export const mockJob = {
companyId: '0003100000',
jobNo: '2023011700001',
customerComment: 'Test- Jan.17,2023',
engineer: {
company: 'Newhaven',
gasSafetyNumber: '145764',
name: 'testsvc',
},
estimatedSymptom: 'A0100004',
jobNotes: '2/1 test',
jobStatus: 'IN_PROGRESS',
photos: [],
product: {
fuel: 'LNG',
id: 'PLCB0021SH001',
name: 'LCB;0021,Kerosene,S,I,GB,CE',
serialNumber: '1134234612123123',
},
reportDate: '2023-02-01T00:12:51.359Z',
serviceRequestDate: '2023-01-17',
}
// addService.test.tsx
describe('AddService', () => {
jest.mock('@react-navigation/native', () => ({
useNavigation: () => {
return { navigate: jest.fn() }
},
useRoute: () => ({
params: {
job: mockJob,
},
}),
})
)
it('should navigate to ServiceParentList', () => {
const navigate = jest.fn()
render(<AddService navigate={navigate} />, {wrapper})
fireEvent.press(screen.getByText('Select a symptom (*)'))
expect(navigate).toHaveBeenCalledWith(
RouteNames.ServiceParentList,
{ job: mockJob }
)
})
})
Verify that the element has the expected value
You can use toHaveTextContent()
it('should render quantity', () => {
const code = '000000000092000044'
const name = 'Shipping Charge'
jest.spyOn(Navigation, 'useRoute').mockReturnValue({
params: {
job: mockJob,
item: { code: code, name: name },
type: ServiceType.PART,
},
key: '',
name: '',
})
render(<AddService />, { wrapper })
fireEvent.press(screen.getByTestId('pressable-plus'))
expect(screen.getByTestId('text-value')).toHaveTextContent('1')
fireEvent.press(screen.getByTestId('pressable-plus'))
expect(screen.getByTestId('text-value')).toHaveTextContent('2')
fireEvent.press(screen.getByTestId('pressable-minus'))
expect(screen.getByTestId('text-value')).toHaveTextContent('1')
})
Verify that the element doesn’t exist
Use queryByText
describe('AddService', () => {
const mockNavigate = jest.fn()
jest.spyOn(Navigation, 'useNavigation').mockReturnValue({
navigate: mockNavigate,
})
jest.spyOn(Navigation, 'useRoute').mockReturnValue({
params: {
job: mockJob,
type: ServiceType.SYMPTOM,
},
key: '',
name: ''
})
it('should filter the list by Gas', () => {
render(<ServiceParentList />, { wrapper })
const searchName = 'Gas Smell/Leak'
fireEvent.changeText(screen.getByTestId('input-search'), searchName)
expect(screen.getByText(searchName)).toBeTruthy()
expect(screen.queryByText('Temperature Fluctuation')).toBeFalsy()
})
})
Verify that the input has the expected value
Use screen.getByDisplayValue()
expect(screen.getByDisplayValue('test')).toBeTruthy()
Comments