
Angular Testing
Ship Angular features with Vitest unit tests, harnesses, router/forms coverage, and E2E setup without guessing project conventions.
Overview
Angular Testing Patterns is an agent skill for the Ship phase that teaches Vitest, harness, router, forms, and E2E testing recipes for Angular apps.
Install
npx skills add https://github.com/analogjs/angular-skills --skill angular-testingWhat is this skill?
- Vitest snapshots, parameterized it.each tables, and fake-timer debounce patterns
- Component harness testing patterns for stable DOM queries
- Dedicated sections for router, reactive/forms, directives, and pipes
- E2E testing setup guidance in the same skill package
- Table-of-contents map across seven Angular testing topic areas
- Seven documented testing topic areas in the table of contents
Adoption & trust: 4.6k installs on skills.sh; 592 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are shipping Angular features but your agent keeps writing inconsistent or incomplete unit and integration tests.
Who is it for?
Solo builders maintaining Angular apps who want Vitest-first unit tests plus harness and router coverage in one skill.
Skip if: Teams not on Angular, or projects that only need a one-line mock without a full testing strategy.
When should I use this skill?
You are writing or extending Angular unit, integration, or E2E tests with Vitest and need consistent patterns.
What do I get? / Deliverables
You get structured Vitest and harness patterns for components, forms, routing, and E2E so new specs match your stack and catch regressions before deploy.
- Vitest spec files
- Harness-based interaction tests
- Router, form, or E2E test scaffolds
Recommended Skills
Journey fit
How it compares
Use instead of ad-hoc “write tests for this component” prompts that ignore harnesses, fake timers, and router isolation.
Common Questions / FAQ
Who is angular-testing for?
Indie and solo developers building Angular web or hybrid apps who delegate test authoring to Claude Code, Cursor, or similar agents.
When should I use angular-testing?
During Ship when adding Vitest specs, snapshot checks, form validators, router navigation tests, or standing up E2E coverage before release.
Is angular-testing safe to install?
Review the Security Audits panel on this Prism page and inspect the skill bundle in your repo before granting filesystem or shell access to your agent.
SKILL.md
READMESKILL.md - Angular Testing
# Angular Testing Patterns ## Table of Contents - [Vitest Advanced Patterns](#vitest-advanced-patterns) - [Component Harnesses](#component-harnesses) - [Testing Router](#testing-router) - [Testing Forms](#testing-forms) - [Testing Directives](#testing-directives) - [Testing Pipes](#testing-pipes) - [E2E Testing Setup](#e2e-testing-setup) ## Vitest Advanced Patterns ### Snapshot Testing ```typescript import { describe, it, expect } from 'vitest'; describe('UserCard', () => { it('should match snapshot', () => { const fixture = TestBed.createComponent(UserCard); fixture.componentRef.setInput('user', { id: '1', name: 'John', email: 'john@example.com' }); fixture.detectChanges(); expect(fixture.nativeElement.innerHTML).toMatchSnapshot(); }); }); ``` ### Parameterized Tests ```typescript import { describe, it, expect } from 'vitest'; describe('Validator', () => { it.each([ { input: '', expected: false }, { input: 'test', expected: false }, { input: 'test@example.com', expected: true }, { input: 'invalid@', expected: false }, ])('should validate email "$input" as $expected', ({ input, expected }) => { expect(isValidEmail(input)).toBe(expected); }); }); ``` ### Testing with Fake Timers ```typescript import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; describe('Debounced Search', () => { beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { vi.useRealTimers(); }); it('should debounce search input', async () => { const fixture = TestBed.createComponent(Search); fixture.detectChanges(); fixture.componentInstance.query.set('test'); // Search not called yet expect(fixture.componentInstance.results()).toEqual([]); // Advance timers vi.advanceTimersByTime(300); await fixture.whenStable(); fixture.detectChanges(); expect(fixture.componentInstance.results().length).toBeGreaterThan(0); }); }); ``` ### Module Mocking ```typescript import { describe, it, expect, vi } from 'vitest'; // Mock entire module vi.mock('./analytics.service', () => ({ Analytics: class { track = vi.fn(); identify = vi.fn(); }, })); describe('with mocked analytics', () => { it('should track events', () => { const fixture = TestBed.createComponent(Dashboard); const analytics = TestBed.inject(Analytics); fixture.detectChanges(); expect(analytics.track).toHaveBeenCalledWith('dashboard_viewed'); }); }); ``` ### Testing Async/Await ```typescript import { describe, it, expect, vi } from 'vitest'; describe('User', () => { it('should load user data', async () => { const mockUser = { id: '1', name: 'Test' }; const httpMock = TestBed.inject(HttpTestingController); const service = TestBed.inject(User); const userPromise = service.loadUser('1'); httpMock.expectOne('/api/users/1').flush(mockUser); const user = await userPromise; expect(user).toEqual(mockUser); }); }); ``` ### Coverage Configuration ```typescript // vite.config.ts export default defineConfig({ test: { coverage: { provider: 'v8', reporter: ['text', 'html', 'lcov'], exclude: [ 'node_modules/', 'src/test-setup.ts', '**/*.spec.ts', '**/*.d.ts', ], thresholds: { statements: 80, branches: 80, functions: 80, lines: 80, }, }, }, }); ``` ### Vitest UI Mode ```bash # Run with UI npx vitest --ui # Open UI at specific port npx vitest --ui --port 51204 ``` ### Concurrent Tests ```typescript import { describe, it, expect } from 'vitest'; // Run tests in this describe block concurrently describe.concurrent('API calls', () => { it('should fetch users', async () => { // ... }); it('should fetch products', async () => { // ... }); it('should fetch orders', async () => { // ... }); }); ``` ### Test Fixtures ```typescript i