@testing-library/react, @testing-library/vue, and @testing-library/angular are framework-specific rendering utilities that allow developers to test components in isolation using DOM queries. jest-dom is a companion library that provides custom Jest matchers to assert state on DOM nodes. Together, they form a complete testing stack where the framework package renders the component and jest-dom validates the output.
Testing frontend components requires two main steps: rendering the component into a test environment and asserting that the output matches expectations. The @testing-library family splits these concerns. The framework-specific packages handle rendering, while jest-dom handles assertions. Let's break down how they work together and where they differ.
Each framework has its own way of mounting components to the DOM. The testing libraries wrap these mechanisms to provide a consistent API.
@testing-library/react uses React's createRoot or render internally.
// @testing-library/react
import { render, screen } from '@testing-library/react';
import { Button } from './Button';
render(<Button label="Submit" />);
expect(screen.getByText('Submit')).toBeInTheDocument();
@testing-library/vue works with Vue component objects or SFCs.
// @testing-library/vue
import { render, screen } from '@testing-library/vue';
import Button from './Button.vue';
render(Button, { props: { label: 'Submit' } });
expect(screen.getByText('Submit')).toBeInTheDocument();
@testing-library/angular integrates with Angular's TestBed.
// @testing-library/angular
import { render, screen } from '@testing-library/angular';
import { ButtonComponent } from './button.component';
await render(ButtonComponent, { componentProperties: { label: 'Submit' } });
expect(screen.getByText('Submit')).toBeInTheDocument();
While the rendering libraries query the DOM, they do not provide custom assertion messages by default. jest-dom fills this gap by extending Jest's expect.
jest-dom adds matchers specifically for DOM nodes.
// jest-dom setup (e.g., in setupTests.js)
import '@testing-library/jest-dom';
// Usage in any test file
import { screen } from '@testing-library/react';
const button = screen.getByRole('button');
expect(button).toBeInTheDocument();
expect(button).not.toBeDisabled();
expect(button).toHaveTextContent('Submit');
Without jest-dom, you would rely on generic matchers that are less descriptive.
// Without jest-dom (less clear)
expect(button).toBeTruthy();
expect(button.disabled).toBe(false);
expect(button.textContent).toContain('Submit');
One of the biggest benefits of this stack is the shared querying API. Regardless of the framework, you query the rendered output the same way.
All Packages support screen and query methods.
getByRole is preferred for accessibility.getByText works for simple text content.findBy variants handle async loading states.// React
const { screen } = require('@testing-library/react');
screen.getByRole('button');
// Vue
const { screen } = require('@testing-library/vue');
screen.getByRole('button');
// Angular
const { screen } = require('@testing-library/angular');
screen.getByRole('button');
This consistency means learning the testing API once applies to all three frameworks. You do not need to relearn how to find elements when switching projects.
Setting up the test environment varies slightly due to framework requirements.
@testing-library/react requires a DOM environment.
jsdom in Jest or Vitest.// jest.config.js for React
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/setupTests.js']
};
@testing-library/vue needs Vue specific globals.
// jest.config.js for Vue
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/setupTests.js']
};
@testing-library/angular requires Angular TestBed config.
BrowserModule or similar.// jest.config.js for Angular
module.exports = {
preset: 'jest-preset-angular',
setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts']
};
jest-dom requires a global import.
setupFilesAfterEnv array.// src/setupTests.js (used by all)
import '@testing-library/jest-dom';
| Package | Primary Role | Framework | Assertion Matchers |
|---|---|---|---|
@testing-library/react | Render React Components | React | No (needs jest-dom) |
@testing-library/vue | Render Vue Components | Vue | No (needs jest-dom) |
@testing-library/angular | Render Angular Components | Angular | No (needs jest-dom) |
jest-dom | DOM Assertions | Any | Yes (extends expect) |
The choice between @testing-library/react, @testing-library/vue, and @testing-library/angular is not a choice at all โ it is dictated by your framework. You cannot use the React library to test a Vue component. The real decision is whether to include jest-dom in your stack.
Include jest-dom if you want readable test failures and standard DOM matchers. It is the industry standard for Testing Library setups.
Skip jest-dom only if you are using a different assertion library like Chai or Vitest's built-in matchers that already cover DOM needs.
Final Thought: These tools work best as a team. Pick the renderer that matches your framework, add jest-dom for assertions, and rely on the shared querying API to keep your tests consistent across your organization.
Choose this package if your project is built with Angular. It handles Angular's change detection and zone.js requirements. It is necessary to render Angular components within the TestBed utility structure.
Choose this package if your project is built with React. It is the official testing utility maintained by the Testing Library team for React components. It handles React-specific rendering concerns like act warnings and hooks cleanup automatically.
Choose this package if your project is built with Vue.js. It integrates with Vue's reactivity system and lifecycle hooks. It is required to properly render Vue components and trigger updates in a test environment.
Choose this package if you are using Jest as your test runner and want readable DOM assertions. It works with any of the framework-specific libraries above. It is not a renderer but adds matchers like toBeInTheDocument to your expect statements.
Simple and complete Angular testing utilities that encourage good testing practices.
You want to write maintainable tests for your Angular components. As a part of this goal, you want your tests to avoid including implementation details of your components and rather focus on making your tests give you the confidence for which they are intended. As part of this, you want your testbase to be maintainable in the long run so refactors of your components (changes to implementation but not functionality) don't break your tests and slow you and your team down.
The @testing-library/angular is a very lightweight solution for
testing Angular components. It provides light utility functions on top of Angular
and @testing-library/dom, in a way that encourages better testing practices. Its
primary guiding principle is:
The more your tests resemble the way your software is used, the more confidence they can give you.
For zoneless applications, Angular Testing Library provides a dedicated slim entry point:
import { render } from '@testing-library/angular/zoneless';
A schematic is available to migrate existing tests:
ng generate @testing-library/angular:migrate-to-zoneless
counter.component.ts
@Component({
selector: 'atl-counter',
template: `
<span>{{ hello() }}</span>
<button (click)="decrement()">-</button>
<span>Current Count: {{ counter() }}</span>
<button (click)="increment()">+</button>
`,
})
export class CounterComponent {
counter = model(0);
hello = input('Hi', { alias: 'greeting' });
increment() {
this.counter.set(this.counter() + 1);
}
decrement() {
this.counter.set(this.counter() - 1);
}
}
counter.component.spec.ts
import { render, screen, fireEvent, aliasedInput } from '@testing-library/angular';
import { CounterComponent } from './counter.component';
describe('Counter', () => {
it('should render counter', async () => {
await render(CounterComponent, {
inputs: {
counter: 5,
// aliases need to be specified this way
...aliasedInput('greeting', 'Hello Alias!'),
},
});
expect(screen.getByText('Current Count: 5')).toBeVisible();
expect(screen.getByText('Hello Alias!')).toBeVisible();
});
it('should increment the counter on click', async () => {
await render(CounterComponent, { inputs: { counter: 5 } });
const incrementButton = screen.getByRole('button', { name: '+' });
fireEvent.click(incrementButton);
expect(screen.getByText('Current Count: 6')).toBeVisible();
});
});
This module is distributed via npm which is bundled with node and
should be installed as one of your project's devDependencies.
Starting from ATL version 17, you also need to install @testing-library/dom:
npm install --save-dev @testing-library/angular @testing-library/dom
Or, you can use the ng add command.
This sets up your project to use Angular Testing Library, which also includes the installation of @testing-library/dom.
ng add @testing-library/angular
You may also be interested in installing jest-dom so you can use
the custom jest matchers.
| Angular | Angular Testing Library |
|---|---|
| 22.x | 19.x |
| 21.x | 19.x |
| 20.x | 18.x, 17.x, 16.x, 15.x, 14.x, 13.x |
| 19.x | 17.x, 16.x, 15.x, 14.x, 13.x |
| 18.x | 17.x, 16.x, 15.x, 14.x, 13.x |
| 17.x | 17.x, 16.x, 15.x, 14.x, 13.x |
| 16.x | 14.x, 13.x |
| >= 15.1 | 14.x, 13.x |
| < 15.1 | 12.x, 11.x |
| 14.x | 12.x, 11.x |
The more your tests resemble the way your software is used, the more confidence they can give you.
We try to only expose methods and utilities that encourage you to write tests that closely resemble how your Angular components are used.
Utilities are included in this project based on the following guiding principles:
At the end of the day, what we want is for this library to be pretty light-weight, simple, and understandable.
Thanks goes to these people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!
jest-dom matcher toHaveFormValues always returns an empty object or there are missing fields. Why?Only form elements with a name attribute will have their values passed to toHaveFormsValues.
Looking to contribute? Look for the Good First Issue label.
Please file an issue for bugs, missing documentation, or unexpected behavior.
Please file an issue to suggest new features. Vote on feature requests by adding a ๐. This helps maintainers prioritize what to work on.
For questions related to using the library, please visit a support community instead of filing an issue on GitHub.
To get started, create a codespace for this repository by clicking this ๐
A codespace will open in a web-based version of Visual Studio Code. The dev container is fully configured with software needed for this project.
Note: Dev containers is an open spec which is supported by GitHub Codespaces and other tools.
MIT