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.
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-forms2. Register matchers
import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless';
import { registerAngularFormMatchers } from '@mazzuco-dev/jest-angular-forms';
setupZonelessTestEnv();
registerAngularFormMatchers();3. Configure Jest
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
| Matcher | Behavior |
|---|---|
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
| Matcher | Behavior |
|---|---|
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
| Matcher | Behavior |
|---|---|
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().