Eda Eren

April 3, 2023
  • React
  • JavaScript
  • Testing

Solving Some Config Issues for Testing React

Now that the Create React App is already dead (and, the new React docs don't even mention it), when using a great tool like Vite, you might realize that it does not come with the usual testing libraries included as opposed to Create React App. While there is Vitest that is powered by Vite itself for unit testing, let's take a look at solving some basic configuration problems for Vite using Jest and Testing Library.

Note that this post is written assuming that you already have a working app, but want to try using Jest and Testing Library. This is of course not the way of test-driven development, but hey, we're just learning.

The very first thing is to install Jest:

npm install --save-dev jest
npm install --save-dev jest

Since we are using Testing Library, we need to install it as well:

npm install --save-dev @testing-library/react
npm install --save-dev @testing-library/react

Of course, we need to add "test": "jest" to "scripts" in our package.json so that we can use npm run test command:

package.json
"scripts": {
"test": "jest"
}
package.json
"scripts": {
"test": "jest"
}

Note that if you are using eslint, we need to add the piece below to our .eslintrc.json (or, your choice of eslint config file) so that you don't get any undefined errors:

.eslintrc.json
"env": {
"jest": true
}
.eslintrc.json
"env": {
"jest": true
}

Considering that we already have an App.jsx, and created an App.test.jsx with the most basic form of something like this:

App.test.jsx
import React from 'react';
import { render, screen } from '@testing-library/react';

import App from './App';

describe('App', () => {
it('renders App component', () => {
render(<App />);
screen.getByRole('heading', { level: 1, name: 'This is a heading' });
});
});
App.test.jsx
import React from 'react';
import { render, screen } from '@testing-library/react';

import App from './App';

describe('App', () => {
it('renders App component', () => {
render(<App />);
screen.getByRole('heading', { level: 1, name: 'This is a heading' });
});
});

If we are quick to run npm run test, we might get some errors related to encountering an "unexpected token." Such as this one:

Jest error: Jest encountered an unexpected token

The hint might be related to Babel, indeed, we need to install Babel to get things working:

npm install --save-dev babel-jest @babel/core @babel/preset-env @babel/preset-react
npm install --save-dev babel-jest @babel/core @babel/preset-env @babel/preset-react

Also, we need to create a babel.config.json and include this in it:

babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
],
["@babel/preset-react", {"runtime": "automatic"}]
]
}
babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
],
["@babel/preset-react", {"runtime": "automatic"}]
]
}

Now, everything is almost okay, but not quite. Let's run npm run test again, and take a look at what we might see this time:

Jest error: Consider using the "jsdom" test environment.

Now, for versions previous to 28, adding this to package.json would have been enough:

package.json
"jest": {
"testEnvironment": "jsdom"
}
package.json
"jest": {
"testEnvironment": "jsdom"
}

But, we need to install jest-environment-jsdom explicitly in order for this to work because Jest removed it from the default package. See this Stack Overflow answer for a better explanation.

So, let's install it as well:

npm install --save-dev jest-environment-jsdom
npm install --save-dev jest-environment-jsdom

Now, everything is alright. Perhaps.

If you have an import statement for, say, a CSS file like the one below in one of your files, you might encounter yet another Jest encountered an unexpected token error:

import 'katex/dist/katex.min.css'; // Or, any kind of css file
import 'katex/dist/katex.min.css'; // Or, any kind of css file

We can use a proxy to mock CSS modules. And yes, we can install yet another package for that:

npm install --save-dev identity-obj-proxy
npm install --save-dev identity-obj-proxy

We also need to update our config. You can add the below in our package.json's jest configuration (depending on the files you have, you might add additional extensions):

package.json
moduleNameMapper: {
"\\.(css|less)$": "identity-obj-proxy"
}
package.json
moduleNameMapper: {
"\\.(css|less)$": "identity-obj-proxy"
}

It is what the Jest docs say about mocking CSS modules as well.

If you don't like your Jest configs being in package.json, you can create a whole new jest.config.json as its own configuration file. This is how it looks like for now:

jest.config.json
{
"testEnvironment": "jsdom",
"moduleNameMapper": {
"\\.(css|less)$": "identity-obj-proxy"
}
}
jest.config.json
{
"testEnvironment": "jsdom",
"moduleNameMapper": {
"\\.(css|less)$": "identity-obj-proxy"
}
}

And, for now, it is enough to get going. Of course, there is never a catch-all solution for all kinds of problems, but it works for my current setting, and I hope someone might find it helpful as well.