DEV Community

Cover image for Effortless Testing Setup for React with Vite, TypeScript, Jest, and React Testing Library
Teyim Asobo
Teyim Asobo

Posted on

Effortless Testing Setup for React with Vite, TypeScript, Jest, and React Testing Library

Recently, I have been learning to write some tests in React using Jest and RTL(React Testing Library), which has been a fun and eye-opening adventure so far. Setting up testing in a Nextjs app which I used to practise testing was not too complicated, and I managed to get things working. I recently wanted to start writing some tests for a project I am working on which is a React+Vitejs, and Typescript application. This proved challenging to set up, resulting in me looking through a few outdated articles here and there, pulling a hair or 2, and almost crying about why the gods of testing were against me. Thankfully, I finally resolved my errors and had my tests running smoothly.

This post therefore serves as an easy-to-follow, updated guide on how to set up testing with Jest and React-testing-library in your Vite + Typescript application.

Challenges faced

While setting up the test in my Vite project, the 2 major problems that are commonly faced are:

  • Configuring Jest to handle SVG images
  • Handling path aliases (absolute imports) with Jest

In this post, we solve the above problems and set up testing in our application. Without wasting much time, let's jump right in.

Setting up a Vite project

Let's get started by creating a simple Vite project. Run the following command to create a React application using Vite: npm create vite@latest . Follow the instructions and select the necessary dependencies

screenshot

Now, cd into your project directory and run npm install to install all dependencies.

Installing Jest and React testing library

Now let's install Jest and RTL and other related dependencies:



npm install -D jest @testing-library/react ts-jest @types/jest ts-node @testing-library/jest-dom jest-environment-jsdom @testing-library/user-event


Enter fullscreen mode Exit fullscreen mode

and wait for the packages to finish installing.

screenshot
Great, now if you open your package.json file, you should see all of these packages as dev dependencies.

Now create a jest.setup.ts file with the following code :



import "@testing-library/jest-dom";


Enter fullscreen mode Exit fullscreen mode

Also, create a jest.config.js file with the following configuration code



export default {
  testEnvironment: "jsdom",
  transform: {
    "^.+\\.tsx?$": "ts-jest",
  },
  setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
};


Enter fullscreen mode Exit fullscreen mode

Handling Styles and SVG's

Now we need 2 more packages for our test to run correctly, the first package is identity-obj-proxy which will help jest transform files with the extension .css, .less, .sass, or .scss. When a file with any of these extensions is imported into your code during testing, Jest will substitute the import with the identity-obj-proxy module. This is a common technique for mocking stylesheets in Jest tests.

The second package we need is jest-transformer-svg. When an SVG file is imported into your code during testing, Jest will substitute the import with the jest-transformer-svg module. This suggests that there might be a custom transformer (jest-transformer-svg) configured for transforming SVG files during the testing process.

To install this package, run:
npm install -D identity-obj-proxy jest-transformer-svg

After successfully installing, modify your jest.config.js file to look like so:



export default {
  testEnvironment: "jsdom",
  transform: {
    "^.+\\.tsx?$": "ts-jest",
  },

  moduleNameMapper: {
    "\\.(css|less|sass|scss)$": "identity-obj-proxy",
    "^.+\\.svg$": "jest-transformer-svg",
  },

  setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
};


Enter fullscreen mode Exit fullscreen mode

Great!!, we have done a lot of configuration, let's write a test and see if things work correctly. Firstly, head over to your package.json and add the following script : "test": "jest"

screenshot

Let's create a simple component that renders a list of users, with some tests for that component. Create a components folder in the src directory and create users folder with a Users.tsx file and a Users.spec.tsx file.

the Users.tsx component looks like this



const Users = () => {
  return (
    <div>
      <h1>Users</h1>
      <ul>
        <li>name 1</li>
        <li>name 2</li>
      </ul>
    </div>
  );
};

export default Users;


Enter fullscreen mode Exit fullscreen mode

Our test for this component in the Users.spec.tsx file looks like this



import { render, screen } from "@testing-library/react";
import Users from "./Users";

describe("User", () => {
  test("renders heading", async () => {
    render(<Users />);
    expect(screen.getByRole("heading", { name: "Users" })).toBeInTheDocument();
  });

  test("renders a list of users", async () => {
    render(<Users />);
    const users = await screen.findAllByRole("listitem");
    expect(users).toHaveLength(2);
  });
});


Enter fullscreen mode Exit fullscreen mode

We have a simple component with some test setup. let's try to run our test using the npm run test command.

screenshot

Our test passes..in some cases, this will fail because of the following Typescript error:

screenshot
We fix this by including our jest.setup.ts file in our tsconfig.json file like so:



{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src", "./jest.setup.ts"],
  "references": [{ "path": "./tsconfig.node.json" }]
}



Enter fullscreen mode Exit fullscreen mode

Specifically, we add ./jest.setup.ts as an array value of the include property. Our Typescript error now disappears and our test runs smoothly.

Handling absolute imports

When setting up our applications, some of our applications might be making use of absolutes imports for clean and readable import statements. However, using absolute imports with Jest will need tiny adjustments, Firstly let's set up absolute import or path aliases in our Vite app and then make it work with Jest. To do that, we need to install the following package

npm install -D vite-tsconfig-paths

After successful installation, we need to modify the vite.config.ts file and the tsconfig.json file. add the following code as part of the compilerOptions property



//absolute import
    "baseUrl": "./src",
    "paths": {
      "@/*": ["./*"]
    }


Enter fullscreen mode Exit fullscreen mode

Our tsconfig.json file looks like this



{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    //absolute import
    "baseUrl": "./src",
    "paths": {
      "@/*": ["./*"]
    }
  },
  "include": ["src", "./jest.setup.ts"],
  "references": [{ "path": "./tsconfig.node.json" }]
}


Enter fullscreen mode Exit fullscreen mode

We also modify our vite.config.ts file



import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), tsconfigPaths()],
});


Enter fullscreen mode Exit fullscreen mode

In your jest.config.ts file, add "^@/(.*)$": "/src/$1" to the moduleNameMapper property.



export default {
  testEnvironment: "jsdom",
  transform: {
    "^.+\\.tsx?$": "ts-jest",
  },

  moduleNameMapper: {
    "\\.(css|less|sass|scss)$": "identity-obj-proxy",
    "^.+\\.svg$": "jest-transformer-svg",
    "^@/(.*)$": "<rootDir>/src/$1",
  },

  setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
};


Enter fullscreen mode Exit fullscreen mode

Amazing!! now you can import components like so:



import Counter from "@/components/counter-two/Counter-two";


Enter fullscreen mode Exit fullscreen mode

using the @ path alias and use them in your test.

Getting test coverage report

Most often when writing tests, we need to know the test coverage. Code coverage reports provide insights into how much of your code is covered by tests. We need a command to Instruct Jest to generate code coverage reports after running the tests. Add the following command to your script in the package.json file



"coverage": "npm test --coverage --watchAll --collectCoverageFrom='src/components/**/*.{ts,tsx}'",


Enter fullscreen mode Exit fullscreen mode

We then run npm run coverage . This runs Jest in watch mode and provides the coverage report after every test run.

screenshot

Conclusion

Setting up tests in React+Vite and Typescript applications is not that straightforward, as we often face a lot of challenges while setting it up. This post serves as an updated, step-by-step, easy-to-follow guide on setting up tests in your React+Vite and Typescript applications.

Code: Github link

Top comments (12)

Collapse
 
sergey_kachaliuk profile image
Sergey Kachaliuk

Thank for the article! Guys if you're getting an error:
"Cannot use JSX unless the '--jsx' flag is provided" then I hope this might help you(worked for me):

I am using Vite + React + TypeScript + Jest. I deleted tsconfig.app.json (comes out of the box) and tsconfig.node.json. So it's only tsconfig.json(code below). And it solved my problem with imports and jsx.

// tsconfig.json
{
    "compilerOptions": {
        "target": "ES2020",
        "useDefineForClassFields": true,
        "lib": ["ES2020", "DOM", "DOM.Iterable"],
        "module": "ESNext",
        "skipLibCheck": true,
        "moduleResolution": "bundler",
        "allowImportingTsExtensions": true,
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
        "jsx": "react-jsx",
        "esModuleInterop": true,
        "strict": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noFallthroughCasesInSwitch": true
    },
    "include": ["src", "./jest.setup.ts"]
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
rajaerobinson profile image
Rajae Robinson

Great post, it's definitely not straightforward to setup unit testing for a React + Vite project. I recently wrote a similar article on this as well; it covers how to setup vitest, RTL, Husky, ESLint & Prettier.

Collapse
 
teyim profile image
Teyim Asobo

Thanks Rajae. Will definitely checkout your article.

Collapse
 
hamza_naeemkhan_b5d12c66 profile image
Hamza Naeem Khan

I have fixed my issue by adding and pinning the versions of jest-environment and jest-config to the same version as jest.

"identity-obj-proxy": "^3.0.0",
"jest": "^27.5.1",
"jest-environment-jsdom": "^27.5.1",

Collapse
 
s1ckgit profile image
Антон Князев

screenshot

I get this error when i try to run tests :(

don't know how to fix this, i've already tried everything :/

Collapse
 
oleksii_malichenko_117267 profile image
Oleksii Malichenko

try to remove offensive words should help!

Collapse
 
theavocadocoder profile image
Kelechi Nwa-uwa

This was really helpful for me. Thanks!

Collapse
 
premkumarg profile image
Prem Kumar G

Thank you, it's working. I got ts error, but after adding "esModuleInterop": true in tsconfig.json it went away.

Collapse
 
buarki profile image
buarki

no fluffs and straight to the point, cheers mate!

Collapse
 
isaachagoel profile image
Isaac Hagoel

Thanks! this was a huge time saver

Collapse
 
teyim profile image
Teyim Asobo

I am happy you found it useful Isaac. :)

Collapse
 
ujjwal_tiwari_ca11e514a2b profile image
Ujjwal Tiwari

it is really good! Saved a lot of time. Thank you so much