React.js Vitest Unit Testing (Husky, lint-staged, ESLint, Prettier)
Tags: webdev, javascript, react, tutorial
Published: 2024-02-04 00:43:30

Creating a React application with Vite has become the preferred way over using create-react-app due to its faster and simpler development environment. However, unlike create-react-app, Vite doesn't come with the necessary packages for unit testing React applications out of the box.

In this guide, we will walk through setting up unit testing for a production-ready React + Typescript Vite application. We'll use the lightweight and fast Vitest testing framework along with the React Testing Library. Additionally, we'll configure linting and formatting using ESLint and Prettier to catch issues early. Lastly, we'll implement Husky and lint-staged to automatically run tests and linting on git commits before code is pushed. By the end, you'll have a robust testing setup to catch bugs, prevent regressions, and maintain code quality standards.

Assuming you already have your React Vite project set up (using `npm create vite@latest`), the first step is to install Vitest:

```bash
npm install -D vitest
```

Then, update your `package.json` file with a test script:

```json
"scripts": {
  "test": "vitest"
}
```

Now, you can write your first test file, for example, `src/components/Example/Example.test.ts`:

```typescript
import { describe, expect, it } from 'vitest';

describe('Example', () => {
 it('adds 1 + 2 to equal 3', () => {
   expect(1 + 2).toBe(3);
 });
});
```

Run the tests with:

```bash
npm run test
```

Vitest will automatically detect and run the tests, providing a fast and straightforward starting point for testing React applications.

## Setting up Vitest with React Testing Library

To test the UI of your React components, you need to interact with the DOM. Here, React Testing Library comes into play. Install the required packages:

```bash
npm i -D jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event
```

Update your Vite configuration file (`vite.config.ts`):

```typescript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/test/setup.ts',
    css: true,
  },
});
```

Create the setup file specified in the configuration (`src/test/setup.ts`):

```typescript
import '@testing-library/jest-dom';
```

Now, you can test the UI of your React application. For example:

```typescript
import { test } from 'vitest';
import { render, screen } from '@testing-library/react';
import App from './App';

test('renders welcome message', () => {
 render(<App />);
 expect(screen.getByText('Learn React')).toBeInTheDocument();
});
```

## Creating a Custom Render for App Providers

If your React application uses the Context API and relies on providers, it's efficient to create a custom render function with the necessary providers. Create a file `utils/test-utils.tsx`:

```typescript
import { cleanup, render } from '@testing-library/react';
import { afterEach } from 'vitest';

afterEach(() => {
 cleanup();
});

const AppProviders = ({ children }: { children: React.ReactNode }) => {
 return (
   <ThemeProvider theme="light">
     {children}
   </ThemeProvider>
 );
}

function customRender(ui: React.ReactElement, options = {}) {
 return render(ui, {
   wrapper: AppProviders,
   ...options,
 });
}

export * from '@testing-library/react';
export { default as userEvent } from '@testing-library/user-event';
export { customRender as render };
```

Now, use this custom render function in your tests when necessary.

## Configuring ESLint and Prettier

For maintaining code quality and consistency, use ESLint for linting and Prettier for formatting. If not already installed, add the necessary packages:

```bash
npm install eslint-plugin-react --save-dev
npm install --save-dev --save-exact prettier
npm install -D eslint-config-prettier
```

Update your ESLint config file:

```javascript
module.exports = {
 extends: [
   'eslint:recommended',
   'plugin:@typescript-eslint/recommended',
   'plugin:react-hooks/recommended',
   'plugin:react/recommended',
   'plugin:react/jsx-runtime',
   'eslint-config-prettier',
 ],
 //...
};
```

Create a Prettier configuration file (`.prettierrc`):

```json
{
 "semi": true,
 "singleQuote": true
}
```

Now, ESLint, Prettier, and tests will automatically run before any commits, ensuring code quality and consistency.

## Setting Up Husky and lint-staged

To automate ESLint, Prettier, and testing on commits, use Husky and lint-staged. Install them:

```bash
npm install --save-dev husky lint-staged
npx husky install
npx husky add .husky/pre-commit "npx lint-staged"
```

Update your `package.json` with lint-staged configurations:

```json
{
 "scripts": {
   "test:staged": "vitest --run"
 },
 "lint-staged": {
   "**/*.{js,jsx,ts,tsx}": [
     "npm run lint",
     "prettier --write --ignore-unknown",
     "npm run test:staged"
   ]
 }
}
```

Now, ESLint, Prettier, and tests will run automatically before commits, ensuring that issues are caught early.

## Conclusion

Setting up Vitest, ESLint, Prettier, Husky, and lint-staged provides a solid foundation for unit testing React apps. For more details, refer to the complete blog post on [BlueSockets](https://bluesockets.com/react/react-js-unit-testing-vitest-husky-lint-staged-eslint-prettier/).