Elevating Testing Skills with Jest and Vite

sabin shrestha
readytowork, Inc.
Published in
6 min readOct 4, 2023
Banner image

What is Jest?

Jest is a popular JavaScript testing framework maintained by Facebook. It’s designed to make testing easy and efficient for JavaScript applications, including React projects. Jest provides a robust set of features for writing unit tests, mocking dependencies, and running tests in parallel.

Jest functions like describing organized test suites, defining individual tests, and expect checks if values match expected results in a concise, automated manner. We will be implementing these functions here in this test. There are lots of other functions. You can check the official doc here.

What is Vite?

Vite is a new-age build tool for JavaScript and TypeScript applications. It’s known for its incredible speed, thanks to its native ES modules handling during development. Vite also offers a fast development server and supports various frameworks, including React.

Setting Up Jest with Vite

Create a new Vite project by running:

npm create vite my-react-app --template react

Install Jest and testing libraries

Install Jest and some essential testing libraries as development dependencies.

npm install --save-dev jest @testing-library/react @testing-library/jest-dom @testing-library/user-event jest-environment-jsdom @babel/preset-env @babel/preset-react

or

yarn add --dev jest @testing-library/react @testing-library/jest-dom @testing-library/user-event jest-environment-jsdom @babel/preset-env @babel/preset-react

Installs several development dependencies for setting up Jest and testing React applications.

Add this to your script in package.json:

"type": "commonjs",
"scripts": {
"test": "jest"
},

You may get eslint error. You can remove the warning by commenting warn in .eslintrc.cjs. since eslint error is outside the scope of this article. 😈

Configure Jest

Create a jest.config.js file in your project's root directory with the following content:

module.exports = {
testEnvironment: "jsdom",
};

testEnvironment: "jsdom": Sets up a browser-like environment for running tests, essential for React components.

Jest is trying to use a native ESM configuration file, but it requires Babel to be set up asynchronously for ESM support. So,

module.exports = {
presets: ["@babel/preset-env", "@babel/preset-react"],
};

Writing Your First Test

Create a test file named App.test.js in the same directory as your component. For instance, if you want to test App.js, create App.test.js. Here's a simple example:

Example 1:

App.jsx

import React from "react";

const App = () => {
return (
<>
<h1>Hello, World!</h1>
</>
);
};
export default App;

App.test.jsx

import React from "react";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import App from "./App";

describe(App, () => {
it("checking if the component renders the document", () => {
render(<App />);
const element = screen.getByText("Hello, World!");
//test fails if the text is different
expect(element).toBeInTheDocument();
});
});

Run Your Tests

You can now run your tests using the following command:

npm test or yarn test

If our screen has “Hello, World!” text then our test passes successfully.

If we were to change the text to something that is not exactly “Hello, World!” in App.jsx file then our test case won’t find the text it's looking for and fails the test. Like so:

Example : 2

Counter.jsx

import React, { useState } from "react";

const Counter = ({ val }) => {
const [count, setCount] = useState(val);
const increaseCount = () => {
setCount((prev) => prev + 1);
};
const decreaseCount = () => {
setCount((prev) => prev - 1);
};
const resetCount = () => {
setCount(0);
};
return (
<div>
//data-testid is an attribute to locate and select elements during testing
using tools like Jest and the @testing-library/react testing library.
Count: <h1 data-testid="counter">{count}</h1>
<button data-testid="increase-button" onClick={increaseCount}>
Increase
</button>
<button onClick={decreaseCount}>Decrease</button>
<button onClick={resetCount}>Reset</button>
</div>
);
};
export default Counter;

Counter.test.jsx

import React from "react";
import Counter from "./Counter";
import { render, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom";

describe(
"Counter Component Tests", // Describing the group of tests for the Counter component
() => {
it("should display the initial value", () => {
// Test case: Checking if the counter component displays the initial value (0)
const { getByTestId } = render(<Counter val={0} />);
const counterElement = Number(getByTestId("counter").textContent);
expect(counterElement).toEqual(0); // Expect the displayed value to be 0
});
},
describe("Counter Increment Tests", () => {
// Describing a sub-group of tests for counter increment functionality
it("should display the initial value", () => {
// Test case: Checking if the counter component displays the initial value (0)
const { getByTestId } = render(<Counter val={0} />);
const counterElement = Number(getByTestId("counter").textContent);
expect(counterElement).toEqual(0); // Expect the displayed value to be 0
});
it("should increase by 1 when the increase button is clicked", () => {
// Test case: Checking if the counter increments by 1 when the button is clicked
const { getByTestId } = render(<Counter val={0} />);
const increaseButton = getByTestId("increase-button");
fireEvent.click(increaseButton); // Simulate a click on the increase button
const counterElement = Number(getByTestId("counter").textContent);
expect(counterElement).toEqual(1); // Expect the displayed value to be 2 after clicking
});

//you can continue adding more tests like decrement, reset, etc.
})
);

The value passed in the `counter` component initially is 0. So, the first test suite checks whether the initial value is 0 or not. If the initial value is 0, the test suite should pass. After the test is passed it should look like this:

If we increment our counter by 2 then our test case should fail.

Example 3

Form.jsx

import React, { useState } from "react";

const Form = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [isValid, setIsValid] = useState(false);

const handleEmailChange = (e) => {
setEmail(e.target.value);
};

const handlePasswordChange = (e) => {
setPassword(e.target.value);
};

const validateInput = () => {
// Updated email validation using regex
const isEmailValid = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/.test(
email
);
const isPasswordValid = password.length > 5;

setIsValid(isEmailValid && isPasswordValid);
};

return (
<div>
<input
type="email"
placeholder="Email"
value={email}
onChange={handleEmailChange}
/>
<br />
<input
type="password"
placeholder="Password"
value={password}
onChange={handlePasswordChange}
/>
<br />
<button onClick={validateInput}>Submit</button>
{isValid ? <p>Form is valid</p> : <p>Form is invalid</p>}
</div>
);
};

export default Form;

Form.test.jsx

import React from "react";
import { render, fireEvent } from "@testing-library/react";
import Form from "./Form";
import "@testing-library/jest-dom";

describe("Login Form component", () => {
it("validates email and password length", () => {
const { getByPlaceholderText, getByText } = render(<Form />);

// Get email and password input fields
const emailInput = getByPlaceholderText("Email");
const passwordInput = getByPlaceholderText("Password");

// Get submit button
const submitButton = getByText("Submit");

// Valid email and password
fireEvent.change(emailInput, { target: { value: "test@example.com" } });
fireEvent.change(passwordInput, { target: { value: "password123" } });
fireEvent.click(submitButton);
expect(getByText("Form is valid")).toBeInTheDocument();

});
});

Here, If we enter a valid email and password with a length greater than 5, the form should be valid. Either one of the failing validation criteria leads the form to be invalid.

In this first test, we entered a valid email and password hence, the test suite passed successfully.

Entered Valid email and password
The test suite passes successfully

However, since our email is not valid and our password length is less than 5 the test suite fails.

Invalid email and password (with length less than 5)
Failed test suite

Conclusion

By integrating Jest with Vite in your React projects, you can ensure the reliability and stability of your codebase. Jest’s simplicity and powerful features, combined with Vite’s speed, create a winning combination for efficient testing and development.

In just a few simple steps, you can have a robust testing setup for your React applications.

Happy testing!

Reference

--

--