Jest matchers for Angular Reactive Forms

The library adds FormGroup-specific assertions to Jest expect. It covers control structure, validators, errors, and values without repeating form.get() calls in every test.

FormGroup

Every matcher receives a FormGroup in expect(form). Register them once in the Jest setup file with registerAngularFormMatchers().

Installation and configuration

Install the package as a development dependency in the project running the tests.

1. Install

npm install --save-dev @mazzuco-dev/jest-angular-forms

2. Register matchers

src/test-setup.ts
import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless';
import { registerAngularFormMatchers } from '@mazzuco-dev/jest-angular-forms';

setupZonelessTestEnv();
registerAngularFormMatchers();

3. Configure Jest

jest.config.ts
export default {
  setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
};

Include the setup file in tsconfig.spec.json as well so TypeScript can see the matcher declarations. If your tests use Zone.js, initialize that environment before calling registerAngularFormMatchers().

Usage examples

Paths follow AbstractControl.get() syntax, including nested groups and FormArray indexes.

Nested controls and FormArray

const form = new FormGroup({
  profile: new FormGroup({ name: new FormControl('', Validators.required) }),
  addresses: new FormArray([
    new FormGroup({ street: new FormControl('', Validators.required) }),
  ]),
});

expect(form).toHaveControls('profile.name', 'addresses.0.street');
expect(form).toHaveRequiredControls(['profile.name', 'addresses.0.street']);

Values and disabled controls

const form = new FormGroup({
  email: new FormControl('ada@example.com'),
  id: new FormControl({ value: 42, disabled: true }),
});

expect(form).toHaveFormValue({ email: 'ada@example.com' });
expect(form).toHaveRawValue({ email: 'ada@example.com', id: 42 });
expect(form).toHaveDisabledControl('id');

Errors and validators

const minLength = Validators.minLength(8);
const password = new FormControl('short', { validators: [minLength] });
const form = new FormGroup({ password });

expect(form).toHaveValidator('password', minLength);
expect(form).toHaveControlError('password', 'minlength');
expect(form).toBeInvalidForm();

API reference

Matchers return an assertion result with a failure message and support .not. Methods taking multiple paths accept strings and arrays of strings.

Structure and state

MatcherBehavior
toHaveControl(path) The control exists at the given path.
toHaveControls(...paths) All specified controls exist.
toHaveEnabledControl(path) The control exists and is enabled.
toHaveDisabledControl(path) The control exists and is disabled.

Validation

MatcherBehavior
toHaveRequiredControl(path) The control has Validators.required.
toHaveRequiredControls(...paths) All controls have Validators.required.
toHaveValidator(path, validator) The control has the same ValidatorFn reference.
toHaveValidators(path, ...validators) The control has all ValidatorFn references.
toHaveControlError(path, error) The control has the error in its current state.
toHaveControlErrors(path, ...errors) The control has all errors in its current state.
toHaveFormError(error) The FormGroup has the error in its current state.
toBeValidForm() The FormGroup is valid.
toBeInvalidForm() The FormGroup is invalid.

Values

MatcherBehavior
toHaveControlValue(path, expected) Deeply compares a control value.
toHaveFormValue(expected) Deeply compares form.value.
toHaveRawValue(expected) Deeply compares form.getRawValue().

Behavior details

Paths
Paths are resolved with FormGroup.get(). Examples: profile.name and addresses.0.street. A missing path fails the assertion.
ValidatorFn
toHaveValidator and toHaveValidators use hasValidator(), which compares function references. Store and reuse the ValidatorFn instance assigned to the control.
hasError()
Error matchers use hasError() against the current validation state. They do not trigger validation themselves.
value / rawValue
toHaveFormValue compares form.value, which omits disabled controls. toHaveRawValue uses getRawValue() and includes them. Jest performs deep equality.
.not
Standard Jest negation works too: expect(form).not.toBeValidForm().
jest-angular-forms

Source code and issue tracking on GitHub.

GitHub ↗