Streamline Your Testing Process: A Step-by-Step Guide to Unit Testing and Snapshot Testing For React Native App with Jest
An Overview of Unit Testing and Snapshot Testing with Jest
React Native provides developers with a flexible platform for building mobile apps that can run on both iOS and Android. When it comes to testing these apps, Jest has become a go-to choice for many developers. Jest is a testing system that is pre-installed with React Native and is quick, simple to use, and dependable. We'll look at using Jest to evaluate React Native applications in this blog post.
Setting Up Jest for React Native Testing
To get started you must install Jest in your project before using it to test React Native. Run the following command in your project directory to accomplish this:
npm install --save-dev @testing-library/jest-native
Once you have installed Jest
, open your package.json folder to update Jest
configuration:
// package.json
{
"preset": "react-native",
"setupFilesAfterEnv": ["@testing-library/jest-native/extend-expect"]
}
Setting up for Expo
npx expo install jest-expo jest
Then update your package.json
"scripts": {
...
"test": "jest"
},
"jest": {
"preset": "jest-expo"
}
Writing Snapshot Tests
Snapshot testing is a technique for testing user interfaces that involves comparing a rendered component's "snapshot" to a previously saved snapshot. If the snapshots match, the test passes; if they don't match, the test fails. Snapshot testing is an effective way to check for unintentional UI changes and regressions.
To get started, let's create our components. To test our component, we will create two separate components - one that represents the component we are testing, and another that serves as the test case.
Now let's create a file and a test file. The test file and our file should be named in the format FileName.test.js
and FileName.js
, respectively. Our test file must be placed in a folder named __tests__
.
Let's test for a Goal Creating App
. Below is our folder structure
__tests__/Layout.test.js
components/Layout.js
Layout.js
import { useState } from "react";
import { Button, StyleSheet, Text, TextInput, View } from "react-native";
export default function Layout() {
const [enteredGoalText, setEnteredGoalText] = useState("");
const [courseGoal, setCourseGoal] = useState([]);
function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}
function addGoalHandler() {
setCourseGoal((currentGoal) => [...currentGoal, enteredGoalText]);
}
return (
<View style={styles.appContainer}>
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your Goals Today"
onChangeText={goalInputHandler}
/>
<Button title="Add Goal" onPress={addGoalHandler} />
</View>
<View style={styles.textContainer}>
{courseGoal.map((goal) => (
<Text key={goal.id}>{goal}</Text>
))}
</View>
</View>
);
}
const styles = StyleSheet.create({
appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
inputContainer: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 24,
borderBottomWidth: 1,
borderBottomColor: "#cccccc",
flex: 1,
},
textInput: {
borderWidth: 1,
width: "70%",
borderColor: "#cccccc",
marginRight: 8,
padding: 10,
},
textContainer: {
flex: 5,
},
});
Let's create our test case. In the test file (Layout.test.js)
, we are going to import our Layout.js component for testing. For testing purposes, we need to use React's test renderer and Jest's snapshot (toMatchSnapshot()
) function. These two functions will work together to capture a snapshot of our rendered component and compare it to a previously saved snapshot."
Below is our test file
Layout.test.js
import React from "react";
import { render, screen, fireEvent } from '@testing-library/react-native';
import Layout from "../components/Layout";
describe("Layout", () => {
test("renders correctly", () => {
const { toJSON } = render(<Layout />);
expect(toJSON()).toMatchSnapshot();
});
});
To test we will Run npm run test
command, and if everything goes well, you should see a snapshot created and one test passed.
Writing Unit Tests
Software development uses unit tests, a sort of automated testing that enables you to test specific sections or functions of your code separately. Unit testing is an essential part of any software development process, and React Native is no exception.
By testing individual units or functions of your code in isolation, you can catch bugs and errors early in the development process, leading to more reliable and efficient code.
Unit Test for input field and button
Let's write unit tests for our previously created component, Layout.js
. We want to ensure that the component contains an input field and a button. We can achieve this by using the getByTestId
function to retrieve the elements with the testID
attributes of "input" and "button". Additionally, we will create a function called toBeDefined
to check if the elements are defined.
To use the testID
attribute in our Layout.js component, we need to update it with appropriate testID
names
Layout.js
import { useState } from "react";
import { Button, StyleSheet, Text, TextInput, View } from "react-native";
export default function Layout() {
const [enteredGoalText, setEnteredGoalText] = useState("");
const [courseGoal, setCourseGoal] = useState([]);
function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}
function addGoalHandler() {
setCourseGoal((currentGoal) => [...currentGoal, enteredGoalText]);
}
return (
<View style={styles.appContainer}>
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your Goals Today"
onChangeText={goalInputHandler}
testID="input"
/>
<Button testID="button" title="Add Goal" onPress={addGoalHandler} />
</View>
<View style={styles.textContainer}>
{courseGoal.map((goal) => (
<Text key={goal.id}>{goal}</Text>
))}
</View>
</View>
);
}
const styles = StyleSheet.create({
appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
inputContainer: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 24,
borderBottomWidth: 1,
borderBottomColor: "#cccccc",
flex: 1,
},
textInput: {
borderWidth: 1,
width: "70%",
borderColor: "#cccccc",
marginRight: 8,
padding: 10,
},
textContainer: {
flex: 5,
},
});
Update theLayout.test.js
component
Layout.test.js
import React from "react";
import { render, fireEvent } from '@testing-library/react-native';
import Layout from "../components/Layout";
describe("Layout", () => {
test("renders correctly", () => {
const { toJSON } = render(<Layout />);
expect(toJSON()).toMatchSnapshot();
});
test('renders input and button', () => {
const { getByTestId } = render(<Layout />);
const input = getByTestId('input');
const button = getByTestId('button');
expect(input).toBeDefined();
expect(button).toBeDefined();
});
});
To test we will Run npm run test
command, and if everything goes well, you should see an output indicating two tests passed.
Test to Check Newly Added Goal
We are going to be creating another test to check when a user input a goal we are going to be using courseGoal
ID to access goal added. This is accomplished by changing the input field's text and simulating a button press using the fireEvent
method. This test checks to see if, when the button is clicked, the component adds a new goal to the list.
Let's update our Layout.js
view with a TestID courseGoal
in other to have access to the input text
Layout.js
import { useState } from "react";
import { Button, StyleSheet, Text, TextInput, View } from "react-native";
export default function Layout() {
const [enteredGoalText, setEnteredGoalText] = useState("");
const [courseGoal, setCourseGoal] = useState([]);
function goalInputHandler(enteredText) {
setEnteredGoalText(enteredText);
}
function addGoalHandler() {
setCourseGoal((currentGoal) => [...currentGoal, enteredGoalText]);
}
return (
<View style={styles.appContainer}>
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Your Goals Today"
onChangeText={goalInputHandler}
testID="input"
/>
<Button testID="button" title="Add Goal" onPress={addGoalHandler} />
</View>
<View testID="courseGoal" style={styles.textContainer}>
{courseGoal.map((goal) => (
<Text key={goal.id}>{goal}</Text>
))}
</View>
</View>
);
}
const styles = StyleSheet.create({
appContainer: {
flex: 1,
paddingTop: 50,
paddingHorizontal: 16,
},
inputContainer: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 24,
borderBottomWidth: 1,
borderBottomColor: "#cccccc",
flex: 1,
},
textInput: {
borderWidth: 1,
width: "70%",
borderColor: "#cccccc",
marginRight: 8,
padding: 10,
},
textContainer: {
flex: 5,
},
});
Let's update out Layout.test.js
with the function to test for the entered goals. we will use a fireEvent.changeText
function to simulate the user typing and fireEvent.press
function to simulate the user clicking the "Add Goal" button. Finally, it will use the expect
function to check that the courseGoal
element has the text content "Learn Jest"
Layout.test.js
import React from "react";
import { render, fireEvent } from '@testing-library/react-native';
import Layout from "../components/Layout";
describe("Layout", () => {
test("renders correctly", () => {
const { toJSON } = render(<Layout />);
expect(toJSON()).toMatchSnapshot();
});
test('renders input and button', () => {
const { getByTestId } = render(<Layout />);
const input = getByTestId('input');
const button = getByTestId('button');
expect(input).toBeDefined();
expect(button).toBeDefined();
});
test('adds goal to list when button is clicked', () => {
const { getByTestId } = render(<Layout />);
const input = getByTestId('input');
const button = getByTestId('button');
const courseGoal = getByTestId('courseGoal');
fireEvent.changeText(input, 'Learn Jest');
fireEvent.press(button);
expect(courseGoal).toHaveTextContent('Learn Jest');
});
});
Finally let's run npm run test
command, and if everything goes well, you should see an output indicating three tests passed.
Conclusion
In conclusion, Jest is a powerful and widely used testing framework that developers can use to test React Native applications. Jest is simple to use, quick, and dependable, and it is pre-installed with React Native, making it a popular choice for testing React Native apps. In this blog post, we explored how to set up Jest for React Native testing and how to use snapshot testing and unit testing to test our components. Snapshot testing is an effective way to check for unintended UI changes and regressions, while unit testing helps catch bugs and errors early in the development process. By using Jest to test your React Native apps, you can ensure that your code is reliable, efficient, and bug-free.