Help us improve
Share bugs, ideas, or general feedback.
From ng
Write Angular unit tests. Use automatically when creating .spec.ts files, when asked to test Angular components/services/pipes/directives/guards, or when reviewing test coverage. Supports Jest and Jasmine/Karma. Mirrors Angular testing documentation.
npx claudepluginhub mayeedwin/angular-plugin --plugin ngHow this skill is triggered — by the user, by Claude, or both
Slash command
/ng:angular-testingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Primary reference**: https://angular.dev/guide/testing — always consult for current testing APIs.
Tests Angular components, services, directives, and pipes using TestBed, ComponentFixture, fakeAsync, and service mocks.
Provides expert Angular/TypeScript patterns for standalone components, signals, RxJS, NgRx state management, smart/dumb components, and performance.
Guides Angular app architecture: standalone components, NgModules, Signals, NgRx, RxJS patterns, lazy loading, dependency injection, Jasmine/Jest testing.
Share bugs, ideas, or general feedback.
Primary reference: https://angular.dev/guide/testing — always consult for current testing APIs.
package.json for jest (Jest) vs karma (Jasmine/Karma)jest.config.js or jest.config.ts (Jest)karma.conf.js (Karma/Jasmine)Use jest.fn() for Jest mocks, jasmine.createSpy() for Jasmine mocks.
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { {Name}Component } from './{name}.component';
describe('{Name}Component', () => {
let component: {Name}Component;
let fixture: ComponentFixture<{Name}Component>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [{Name}Component], // standalone: import directly, not declarations
}).compileComponents();
fixture = TestBed.createComponent({Name}Component);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { {Name}Component } from './{name}.component';
import { {Name}Service } from '../services/{name}.service';
// Jest
const mockService = {
getData: jest.fn().mockReturnValue(of([])),
};
// Jasmine
// const mockService = jasmine.createSpyObj('{Name}Service', ['getData']);
describe('{Name}Component', () => {
let component: {Name}Component;
let fixture: ComponentFixture<{Name}Component>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [{Name}Component],
providers: [
{ provide: {Name}Service, useValue: mockService },
],
}).compileComponents();
fixture = TestBed.createComponent({Name}Component);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should load data on init', () => {
expect(mockService.getData).toHaveBeenCalled();
});
});
it('should update count signal', () => {
component.increment();
fixture.detectChanges();
expect(component.count()).toBe(1);
});
import { TestBed } from '@angular/core/testing';
import { {Name}Service } from './{name}.service';
describe('{Name}Service', () => {
let service: {Name}Service;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject({Name}Service);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting, HttpTestingController } from '@angular/common/http/testing';
import { {Name}Service } from './{name}.service';
describe('{Name}Service', () => {
let service: {Name}Service;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
provideHttpClient(),
provideHttpClientTesting(),
],
});
service = TestBed.inject({Name}Service);
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => httpMock.verify());
it('should fetch data', () => {
const mockData = [{ id: 1 }];
service.getAll().subscribe(data => {
expect(data).toEqual(mockData);
});
const req = httpMock.expectOne('/api/{name}');
expect(req.request.method).toBe('GET');
req.flush(mockData);
});
});
import { {Name}Pipe } from './{name}.pipe';
describe('{Name}Pipe', () => {
const pipe = new {Name}Pipe();
it('should transform value', () => {
expect(pipe.transform('input')).toBe('expected-output');
});
it('should handle null/undefined', () => {
expect(pipe.transform(null)).toBe('');
});
});
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Component } from '@angular/core';
import { {Name}Directive } from './{name}.directive';
@Component({
standalone: true,
imports: [{Name}Directive],
template: `<div app{Name}>test</div>`,
})
class TestHostComponent {}
describe('{Name}Directive', () => {
let fixture: ComponentFixture<TestHostComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TestHostComponent],
}).compileComponents();
fixture = TestBed.createComponent(TestHostComponent);
fixture.detectChanges();
});
it('should apply directive', () => {
const el = fixture.nativeElement.querySelector('[app{Name}]');
expect(el).toBeTruthy();
});
});
import { {name}Reducer, initialState } from './reducer';
import * as {Name}Actions from './actions';
describe('{name}Reducer', () => {
it('should set loading true on load action', () => {
const state = {name}Reducer(initialState, {Name}Actions.load{Name}());
expect(state.loading).toBe(true);
});
it('should populate data on success', () => {
const data = [{ id: 1 }];
const state = {name}Reducer(initialState, {Name}Actions.load{Name}Success({ data }));
expect(state.data).toEqual(data);
expect(state.loading).toBe(false);
});
});
import { select{Name}Data } from './selectors';
describe('{name} selectors', () => {
const state = { data: [{ id: 1 }], loading: false, error: null };
it('should select data', () => {
expect(select{Name}Data.projector(state)).toEqual(state.data);
});
});
import { TestBed } from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import { Observable, of } from 'rxjs';
import { cold, hot } from 'jest-marbles'; // or 'jasmine-marbles'
import { load{Name}Effect } from './effects';
import { {Name}Service } from '../services/{name}.service';
import * as {Name}Actions from './actions';
describe('load{Name}Effect', () => {
let actions$: Observable<any>;
const mockService = { getAll: jest.fn() };
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
provideMockActions(() => actions$),
{ provide: {Name}Service, useValue: mockService },
],
});
});
it('should dispatch success on load', () => {
const data = [{ id: 1 }];
mockService.getAll.mockReturnValue(of(data));
actions$ = hot('-a', { a: {Name}Actions.load{Name}() });
const expected = cold('-b', { b: {Name}Actions.load{Name}Success({ data }) });
expect(TestBed.runInInjectionContext(() => load{Name}Effect())).toBeObservable(expected);
});
});
import { TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { {name}Guard } from './{name}.guard';
import { AuthService } from '@core/services/auth.service';
describe('{name}Guard', () => {
let authService: jest.Mocked<AuthService>;
let router: jest.Mocked<Router>;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{ provide: AuthService, useValue: { isAuthenticated: jest.fn() } },
{ provide: Router, useValue: { navigate: jest.fn() } },
],
});
authService = TestBed.inject(AuthService) as jest.Mocked<AuthService>;
router = TestBed.inject(Router) as jest.Mocked<Router>;
});
it('should allow access when authenticated', () => {
authService.isAuthenticated.mockReturnValue(true);
const result = TestBed.runInInjectionContext(() =>
{name}Guard({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot)
);
expect(result).toBe(true);
});
});
afterEach(() => httpMock.verify()) when using HttpTestingControllerfixture.detectChanges() after state mutations in component tests.projector() — no store setup neededTestBed.runInInjectionContext() to test functional guards/effects