# @testing-library/angular: Angular Tests Written the Way Users Drive the UI

> Angular Testing Library is an MIT licensed set of light utilities over Angular and @testing-library/dom that pushes tests toward querying by role and label instead of component internals, with a separate zoneless entry point and a migration schematic. It suits teams whose component tests break on every refactor.

**testing-library/angular-testing-library** — 🐙 Simple and complete Angular testing utilities that encourage good testing practices

- Repository: https://github.com/testing-library/angular-testing-library
- Website: https://testing-library.com/angular
- Stars: 794 · Forks: 95
- Language: TypeScript
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/testing-library-angular-testing-library

## The problem it names is refactor breakage

The readme states the problem in the first person, and it is worth quoting the shape of it. You want to write maintainable tests for your Angular components, and as part of that 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. The consequence you are trying to avoid is that your test base stops being maintainable in the long run, so refactors of your components, meaning changes to implementation but not functionality, break your tests and slow you and your team down. That is a specific kind of technical debt, and it is worth noticing that it is not about coverage. A test that asserts on a private field can have full line coverage and still give you no confidence, because it fails on a change that preserved behaviour and passes on a change that broke it.

## A thin layer over Angular and the DOM testing library

The solution is described as a very lightweight solution for testing Angular components, providing light utility functions on top of Angular and @testing-library/dom in a way that encourages better testing practices. The word lightweight is doing real work here. There is no custom test framework, no assertion library and no mocking system; what you get is a render function and query helpers that speak the same vocabulary as the DOM testing library the JavaScript ecosystem already uses. The primary guiding principle is the one the whole project is built on: the more your tests resemble the way your software is used, the more confidence they can give you. The guiding principles section says the project tries to expose only methods and utilities that encourage good habits, which is a curation decision as much as a technical one.

## A counter test, written against the rendered output

The example is the clearest statement of the philosophy, and it is worth reading closely. The component under test uses a signal-based model for the counter and a signal input with an alias for the greeting, and renders a span with the greeting, a decrement button, a count span and an increment button. The test file imports render, screen, fireEvent and aliasedInput, and the first case renders the component with an input value of 5 and an aliased input, then asserts on visible text.

```typescript
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();
  });
```

The second case finds the increment button by its role and accessible name and fires a click, then checks the count text. Nothing touches the component instance.

## Zoneless has its own entry point and its own schematic

For zoneless applications, Angular Testing Library provides a dedicated slim entry point rather than reusing the main one.

```ts
import { render } from '@testing-library/angular/zoneless';
```

That is a deliberate split, and the reason is that a zoneless application changes how change detection is triggered during a test, so the harness has to be set up differently rather than being configured differently. A schematic is available to migrate existing tests.

```bash
ng generate @testing-library/angular:migrate-to-zoneless
```

The existence of a migration schematic is the practical signal here. It means the library treats the move from zone-based to zoneless testing as a mechanical transformation you can apply across a suite, which is a much better position to be in than rewriting every test by hand, and it also means the library is tracking the direction Angular is taking rather than lagging behind it.

## Installing it and the version matrix you have to check

The package is distributed via npm and should be installed as one of your project's devDependencies. From version 17 of Angular Testing Library you also need @testing-library/dom installed alongside it.

```bash
npm install --save-dev @testing-library/angular @testing-library/dom
```

There is also the Angular CLI schematic, which sets up the project to use the library and includes the installation of @testing-library/dom.

```bash
ng add @testing-library/angular
```

The version compatibility table is the part to check before you commit, because the mapping is not one to one. Angular 22.x and 21.x pair with Angular Testing Library 19.x, Angular 20.x pairs with 18.x down to 13.x, Angular 19.x and 18.x pair with 17.x down to 13.x, and Angular 16.x pairs with 14.x and 13.x. Angular 15.1 and above pairs with 14.x and 13.x, while anything below 15.1 is on 12.x or 11.x. In other words, older Angular versions are stuck on old versions of the library, which is worth knowing before you plan an Angular upgrade and a test rewrite at the same time.

## The reactive forms gotcha the FAQ documents

The FAQ carries a single specific entry, and it is a real trap rather than a filler question. If you are using Reactive Forms and the jest-dom matcher toHaveFormValues always returns an empty object or there are missing fields, the readme explains why in a way worth knowing. The matcher reads the form state as the DOM sees it, and with reactive forms the model is not synchronised with the DOM in the way the matcher expects, so the values come back empty or incomplete. The answer points to installing jest-dom for the custom matchers, and the whole entry is a reminder of the ecosystem's assumption that you query the rendered form rather than the form model. The readme also carries a Codespaces badge so you can open the repository in a preconfigured environment, which is convenient for trying a pull request without setting up an Angular workspace by hand.

## How the library itself is built and tested

The repository is an Angular workspace with the library in projects/ and two example applications under apps/, one of which is a Jest app and another a Karma app, with separate test scripts for each. That pairing is deliberate: it means the library is exercised under two different test runners, which catches the class of bug that only appears in one runner's environment handling. The scripts show the rest of the workflow, with an ng build for the library followed by a separate build of the schematics through tsc, tests run with ng test for the library and each example app, linting across the library and the apps, prettier for formatting with a separate check mode, and lint-staged wired to a pre-commit hook that installs itself by setting the git hookspath. The package version is managed by semantic-release, with the version string set to a placeholder that the release tooling replaces, so the published version is derived from commit messages rather than edited by hand.

## Where this sits against the older ways of testing Angular

The alternative is the standard Angular TestBed approach, where you create a component fixture, reach into the component instance, call lifecycle methods by hand and assert on component properties. That is not a bad way to test, and for a component with heavy internal logic it can be the right one. The difference in approach is what gets asserted on. TestBed assertions are about the component's state, which means a change in how the component stores state breaks the test even when the screen looks identical, and it means the test tells you about the implementation rather than about the behaviour. Querying by role and by accessible name inverts that, so the test states what a user can perceive. The cost is real and worth naming: some assertions become harder, since there is no longer a component field to check, and a component that renders nothing meaningful to a screen reader needs a test id, which is a concession the guiding principles do not pretend away.

## Conclusion

Angular Testing Library fits a team whose component tests are coupled to implementation details and break on refactors, and who can restructure their tests to query by accessible name. It is the wrong choice if you need to assert on component state or private methods directly, since that is exactly what it discourages, and the wrong choice if you are still on Angular below 15.1, which the version matrix shows is out of range. Before you convert a suite, run one component through the zoneless entry point and confirm your test runner reports change detection the way your application does.

## FAQ

### What problem does Angular Testing Library solve?

It lets you write component tests that avoid implementation details, so refactors that change implementation without changing functionality do not break your test base. The guiding principle is that the more your tests resemble the way your software is used, the more confidence they can give you.

### How do I install Angular Testing Library?

Run npm install --save-dev @testing-library/angular @testing-library/dom, since version 17 of the library also needs the DOM testing library. Alternatively use ng add @testing-library/angular, which sets up the project and installs @testing-library/dom for you.

### How do I test a zoneless Angular application with it?

Import render from the dedicated zoneless entry point, @testing-library/angular/zoneless. There is also a schematic, ng generate @testing-library/angular:migrate-to-zoneless, for converting existing tests.

### Which Angular versions does the current release support?

Angular 22.x and 21.x pair with Angular Testing Library 19.x, Angular 20.x with 18.x down to 13.x, and Angular 19.x and 18.x with 17.x down to 13.x. Anything below Angular 15.1 is limited to 12.x or 11.x of the library.

### Why does toHaveFormValues return an empty object with reactive forms?

The jest-dom matcher reads the form state as the DOM reflects it, and reactive forms do not synchronise the model that way. The readme documents this as a known case, and installing jest-dom for the custom matchers is the recommended step.

## Sources

- [License: MIT](https://github.com/testing-library/angular-testing-library/blob/main/LICENSE)
- [Project website](https://testing-library.com/angular)
- [README](https://github.com/testing-library/angular-testing-library/blob/main/README.md)
- [Releases](https://github.com/testing-library/angular-testing-library/releases)
- [testing-library/angular-testing-library on GitHub](https://github.com/testing-library/angular-testing-library)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/testing-library-angular-testing-library
