Ir al contenido

ADR-009-a — Frontend Testing Strategy (Angular 21)

ADR-009-a — Frontend Testing Strategy (Angular 21)

Sección titulada «ADR-009-a — Frontend Testing Strategy (Angular 21)»

Draft

[[adr-009-testing-strategy|ADR-009]] covers the project-wide testing philosophy; [[adr-009-b-backend-testing-strategy|ADR-009-b]] covers the backend. This document decides the frontend-specific rules for Angular 21 with Vitest.

Three Test Layers. The anti-redundancy rule ([[adr-009-testing-strategy|ADR-009]] §3) applies across all layers.

LayerOwnsExample
Component logicSignal graph, computed values, service HTTP contractsoccupancyColor(null) returns surface token
Template complianceDesign system rules via static string analysis on source filesNo dark:, no *ngIf, uses inject()
Architectural guardsCodebase-wide structural invariantsfeatures/ never imports shared/mocks/ data

Signal Testing Rules. Test the signal graph, not the template rendering. Computed signals are pure functions — test them as such.

PrimitiveRule
signal() / computed()Replicate the pure function, test with explicit inputs. No TestBed.
linkedSignal()Same as computed; or TestBed + flushEffects() if parent-dependent.
effect()TestBed + flushEffects(). Don’t assert execution count.
httpResource()HttpTestingController to flush, assert resulting signal values.

Code examples in [[testing|docs/dev/testing.md]].

Component Test Rules.

ScenarioApproach
Pure computed logicDirect function replica — no TestBed
Service with HTTPTestBed + provideHttpClientTesting()
Component with input.required<>()TestBed when DOM needed; otherwise replica
Guard or interceptorTestBed with provideRouter stubs
  • Do not test PrimeNG internal rendering — test our data bindings and signal logic only.
  • OnPush: always fixture.detectChanges() explicitly after mutations.
  • No snapshot tests.

Design System Enforcement. Static string analysis on component .ts source files. Runs on every component.

ForbiddenReason
dark: CSS classPrimeNG tokens handle dark mode ([[adr-013-accessibility
*ngIf / *ngForUse @if / @for
Raw Tailwind colors (text-red-500)Use PrimeNG tokens
@Input() / @Output()Use input() / output() signal API
Missing OnPushAll components must declare it

Architectural Guards.

GuardEnforces
no-mock-imports.spec.tsfeatures/ has no runtime mock data imports ([[adr-022-mock-data-scope
no-subscribe.spec.ts (proposed)features/ has no .subscribe() — state lives in signals
app.routes.spec.tsRoute paths in English, breadcrumbs in Spanish ([[adr-001-b-language-conventions

Mock Data Discipline. Per [[adr-022-mock-data-scope|ADR-022]]: mocks allowed in shared/mocks/*.spec.ts and showcase/. Forbidden in features/ and core/ — use HttpTestingController. External image URLs forbidden.

Coverage Threshold. 80% (statements, branches, functions, lines) via v8 provider in vitest.config.ts. Drops below → CI fails.

What We Do Not Test. PrimeNG rendering, snapshots, E2E flows ([[adr-009-d-bdd-e2e-testing|ADR-009-d]]), accessibility automation (pending), Angular change detection.

  • (+) Three-layer model eliminates ambiguity about test placement.
  • (+) Architectural guards are extensible — same pattern, no new infrastructure.
  • (+) Template compliance runs on every component automatically.
  • (-) Function replica pattern requires discipline to stay in sync with component logic.
  • (-) Hour-related tests need TZ=UTC or vi.setSystemTime() for reproducibility ([[adr-015-datetime-timezone|ADR-015]]).