Jses.ioby Shaojiang

Jest: define the expected `expect` variable

Shaojiang

Date:

Jest: define the expected `expect` variable

In a react application, there are too many unit testing framework to choose. During development of MyEx.AI, I encountered the problem of using correct `expect` variable by default. Here is a brief fix.

1. Unit test Next.js with Jest and @testing-library

MyEx.AI is using Next.js, basically I follow the official instructions to use Jest + @testing-library. According to the React offical doc:

Jest is a JavaScript test runner that lets you access the DOM via jsdom. While jsdom is only an approximation of how the browser works, it is often good enough for testing React components. Jest provides a great iteration speed combined with powerful features like mocking modules and timers so you can have more control over how the code executes.

React Testing Library is a set of helpers that let you test React components without relying on their implementation details. This approach makes refactoring a breeze and also nudges you towards best practices for accessibility. Although it doesn’t provide a way to “shallowly” render a component without its children, a test runner like Jest lets you do this by mocking.

Resolve `expect` conflict with cypress

If your Next.js has cypress like mine, it's likely to introduce a conflict of the expect variable like:

That's because, since cypress 10, cypress uses Chai as the default testing assertion tool. It will conflict with Jest. The best solution comes from this GitHub issue: New Cypress 10 global TypeScript type conflicts with Jest expect: exclude cypress.config.ts in the tsconfig.json:

1{
2 "compilerOptions": {"target": "es5"...},
3 "include": [...],
4 "exclude": [
5 "node_modules",
6 "./cypress.config.ts"
7 ]
8}

Then there is only one expect left:

2. Extended expect from @testing-library

@testing-library/jest-dom includes a set of convenient custom matchers  such as .toBeInTheDocument() making it easier to write tests. You can import the custom matchers for every test by adding the following option to the Jest configuration file:
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts']

Where jest.setup.ts should have content:

1// Learn more: https://github.com/testing-library/jest-dom
2import '@testing-library/jest-dom/jest-globals';

Please note that instead of import '@testing-library/jest-dom', we should use import '@testing-library/jest-dom/jest-globals';. For details please checkout the release note of @testing-library v6.0.0.

3. Use specified expect in test cases

Finally, in test case files like Card.test.tsx, import the extended expect and use it like:

1import { expect } from '@jest/globals';
2import { render, screen } from '@testing-library/react';
3
4import Card from './Card';
5
6describe('Home', () => {
7 it('renders a heading', () => {
8 const { container } = render(<Card label='Card Header'>whatever</Card>);
9
10 const heading = screen.getByRole('heading', {
11 name: /Card Header/i,
12 });
13
14 expect(heading).toBeInTheDocument();
15 expect(container).toMatchSnapshot();
16 });
17});

4. Pending ...

This is still under investigation: if I do not import expect explicitely, the default global expect is not the extended one.