2026-05-11 19:38:21 -04:00

4.4 KiB

paths
paths
**/*.spec.ts
**/*.test.ts

Angular Testing

This file extends common/testing.md with Angular specific content.

Test Runner

Use the test runner configured by the project. Check angular.json and package.json; Angular projects commonly use Vitest, Jest, or Jasmine + Karma.

ng test               # watch mode
ng test --no-watch    # CI mode

TestBed Setup

For standalone components, import the component directly. Call compileComponents() for components with external templates.

describe('UserCardComponent', () => {
  let fixture: ComponentFixture<UserCardComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [UserCardComponent],
    }).compileComponents();

    fixture = TestBed.createComponent(UserCardComponent);
  });
});

Signal Inputs

Set signal-based inputs via fixture.componentRef.setInput():

fixture.componentRef.setInput('user', mockUser);
fixture.detectChanges();

Component Harnesses

Prefer Angular CDK component harnesses over direct DOM queries for UI interaction. Harnesses are more resilient to markup changes.

import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatButtonHarness } from '@angular/material/button/testing';

let loader: HarnessLoader;

beforeEach(() => {
  loader = TestbedHarnessEnvironment.loader(fixture);
});

it('triggers save on button click', async () => {
  const button = await loader.getHarness(MatButtonHarness.with({ text: 'Save' }));
  await button.click();
  expect(saveSpy).toHaveBeenCalled();
});

Router Testing

Use RouterTestingHarness for components that depend on the router:

import { RouterTestingHarness } from '@angular/router/testing';

it('renders user on navigation', async () => {
  const harness = await RouterTestingHarness.create();
  const component = await harness.navigateByUrl('/users/1', UserDetailComponent);
  expect(component.userId()).toBe('1');
});

Async Testing

Use fakeAsync + tick for controlled async. Use waitForAsync for real async with fixture.whenStable().

it('loads user after delay', fakeAsync(() => {
  const service = TestBed.inject(UserService);
  vi.spyOn(service, 'getUser').mockReturnValue(of(mockUser));

  fixture.detectChanges();
  tick();
  fixture.detectChanges();

  expect(fixture.nativeElement.querySelector('.name').textContent).toBe(mockUser.name);
}));

HTTP Testing

import { provideHttpClientTesting } from '@angular/common/http/testing';
import { HttpTestingController } from '@angular/common/http/testing';

beforeEach(() => {
  TestBed.configureTestingModule({
    providers: [provideHttpClient(), provideHttpClientTesting()],
  });
  httpMock = TestBed.inject(HttpTestingController);
});

afterEach(() => httpMock.verify());

Service Testing

Inject services directly without a component fixture:

describe('UserService', () => {
  let service: UserService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [provideHttpClient(), provideHttpClientTesting()],
    });
    service = TestBed.inject(UserService);
  });
});

What to Test

  • Services: All public methods, error paths, HTTP interactions
  • Components: Input/output bindings, rendered output for key states, user interactions via harnesses
  • Pipes: Pure transformation — plain unit tests, no TestBed needed
  • Guards/Resolvers: Return values for allowed and denied states using RouterTestingHarness

E2E Testing

Use the project's configured E2E framework, such as Cypress or Playwright, for critical user flows.

describe('Login flow', () => {
  it('redirects to dashboard on valid credentials', () => {
    cy.visit('/login');
    cy.get('[data-cy=email]').type('user@example.com');
    cy.get('[data-cy=password]').type('password123');
    cy.get('[data-cy=submit]').click();
    cy.url().should('include', '/dashboard');
  });
});
  • Add data-cy attributes to interactive elements for stable selectors
  • Do not rely on CSS classes or text content for selectors in E2E tests

Coverage

Target ≥80% for services and pipes. Components: test behaviour, not implementation details.

Skill Reference

See skill: angular-developer for comprehensive testing patterns, harness usage, and async best practices.