angular-testing
Angular Testing with Jest
Test Angular v21+ applications with Jest and @testing-library/angular, prioritizing user-centric testing over implementation details.
Core Principles
- User-Centric Testing: Simulate real user behavior using
@testing-library/angular. Avoid testing implementation details or private state. - Modern Angular: Follow Angular v20+ standards (Standalone components, Signals,
@if/@forcontrol flow). - Accessibility: Use semantic queries (getByRole, getByLabelText) that promote accessible markup.
Framework & Syntax
Jest Globals
Always use Jest globals:
describe,it,expect,jest(e.g.,jest.fn(),jest.spyOn())- Never use
jasmine,spyOn, ordone()
Testing Library DOM Matchers
Use @testing-library/jest-dom matchers for better assertions:
// Good - Use jest-dom matchers
expect(button).toHaveClass('primary');
expect(text).toBeVisible();
expect(element).toHaveTextContent('Hello');
expect(button).toBeDisabled();
// Bad - Avoid direct DOM manipulation
expect(element.classList.contains('name')).toBe(true);
expect(element.style.display).toBe('none');
Test Structure (AAA Pattern)
Strict AAA Guidelines
Structure every test into Arrange, Act, and Assert blocks:
- Leave exactly one empty line between Arrange/Act/Assert blocks
- Do NOT include
// Arrangeor// Actor// Assertcomments - Use meaningful test titles with active voice
Test Naming
- Use active voice, describe what the test does
- Avoid "should" phrasing
// Bad - Generic "should" title
it('should handle submit', () => { });
// Good - Descriptive and specific
it('prevents submission when the email field is invalid', () => { });
// Good - Clear behavior description
it('updates the profile when the save button is clicked', () => { });
Component Testing Strategy
Isolation
Each it block must be self-contained. Use beforeEach only for setup truly shared across all tests.
Query Selection
Use Testing Library queries in order of preference:
screen.getByRole()- Most accessible and semanticscreen.getByLabelText()- For form elementsscreen.getByText()- For static text contentscreen.getByTestId()- Last resort only
User Interactions
Use userEvent from @testing-library/user-event for realistic interactions instead of native DOM events.
Public API Testing
Test only public fields and methods. Test protected or private logic only through public triggers (user interactions, input changes, public method calls).
Business Logic Focus
Do not test Angular's built-in directives (like @if, @for). Test the component's unique inputs, outputs, and business logic.
Mocking & Async
Effective Mocking
Use jest.spyOn() or jest.fn() to isolate dependencies. Avoid importing heavy modules; mock services and APIs at the boundary.
Async Handling
Use async/await for all promises returned by queries or events. Never leave a promise unhandled.
Signals
Update signal state via component.mySignal.set() and verify the UI updates automatically.
HTTP
Use HttpTestingController and provideHttpClientTesting() for service tests.
Best Practices Summary
- User-Centric: Test what users see and interact with
- AAA Pattern: Arrange, Act, Assert with clear separation
- Meaningful Names: Active voice, specific behavior
- Isolation: Each test is self-contained
- Accessibility: Use semantic queries (getByRole, getByLabelText)
- Public API Only: Don't test private implementation
- Async Handling: Always await promises and user events
- Mock at Boundaries: Mock services, not internal logic
- Business Logic Focus: Don't test Angular's built-in directives
- DOM Matchers: Use jest-dom for readable assertions
For complete usage examples and patterns, see references/testing-jest-patterns.md.