on unit test creation in a react native proyects
This document outlines the standards and best practices for writing unit tests in this React Native project. Our testing strategy is centered around React Native Testing Library, which encourages testing application behavior from a user's perspective.
All tests should adhere to the following principles:
[FileName].test.tsx.Example:
/src/components/MyComponent/
āāā index.tsx
āāā MyComponent.test.tsx
All tests must follow the Given When Then naming convention to ensure clarity and consistency:
Format: "Given [context], when [action], then [expected outcome]"
Examples:
describe("LoginButton", () => {
it("Given a valid user credential, when the login button is pressed, then it should call the login function", () => {
// Test implementation
});
it("Given an invalid email format, when the user submits the form, then it should display an error message", () => {
// Test implementation
});
it("Given a loading state, when the component renders, then it should display a loading spinner", () => {
// Test implementation
});
});
Use the queries from React Native Testing Library to find elements on the screen. Prioritize queries in the following order, as they reflect what the user sees:
getByRole / findByRole / queryByRole: For elements with an accessibilityRole.getByText / findByText / queryByText: For elements with text content.getByPlaceholderText: For input fields.getByTestId: As a last resort for elements that cannot be queried by other means. Avoid overusing testID.When testing asynchronous behavior (e.g., data fetching), use findBy* queries or the waitFor utility.
it("should display the user name after fetching data", async () => {
// Arrange
mockedAxios.get.mockResolvedValue({ data: { name: "John Doe" } });
render(<UserProfile />);
// Act & Assert
const userName = await screen.findByText("John Doe");
expect(userName).toBeOnTheScreen();
});
Use fireEvent or @testing-library/user-event to simulate user interactions.
import { render, screen, fireEvent } from "@testing-library/react-native";
it("should call onPress when the button is pressed", () => {
// Arrange
const onPressMock = jest.fn();
render(<Button title="Submit" onPress={onPressMock} />);
// Act
fireEvent.press(screen.getByText("Submit"));
// Assert
expect(onPressMock).toHaveBeenCalledTimes(1);
});
accessibilityRole, accessibilityLabel, and other a11y props.react-navigation or axios work correctly. Focus on testing how your code integrates with them.Our test environment (__jest__/setup.ts) pre-configures mocks for many common modules (react-navigation, axios, Nexus libraries, etc.).
For specific API responses in a test, mock the implementation of the data-fetching function (e.g., axios.get or nxGetData).
import axios from "axios";
jest.mock("axios");
const mockedAxios = axios as jest.Mocked<typeof axios>;
it("should handle API failure gracefully", async () => {
// Arrange
mockedAxios.get.mockRejectedValue(new Error("Network Error"));
render(<MyComponent />);
// Assert
expect(await screen.findByText("Something went wrong")).toBeOnTheScreen();
});
If a component relies on a complex custom hook, you can mock the hook to simplify the test setup and focus on the component's behavior.
import * as useMyHook from "../hooks/useMyHook";
it("should display a loading state from the hook", () => {
// Arrange
jest
.spyOn(useMyHook, "default")
.mockReturnValue({ isLoading: true, data: null });
render(<MyComponent />);
// Assert
expect(screen.getByTestId("loading-spinner")).toBeOnTheScreen();
});