angular/angular
Testing
Angular's tests run on Bazel. Different test types use different runners under the hood, but the entry point is always pnpm bazel test //… or one of the wrapper scripts in package.json.
Test surfaces
| Test kind | Runner | Where it lives |
|---|---|---|
| Unit (browser) | Karma + Jasmine | packages/*/test/** and *.spec.ts |
| Unit (node) | Jasmine via nodejs_test |
tools, compiler-cli, devtools backend |
| Template type-check tests | Custom diff harness | packages/compiler-cli/test/ngtsc/diagnostics |
| Public API goldens | node goldens/public-api/manage.js test |
goldens/public-api/*.d.ts |
| Bundle-size goldens | tools/symbol-extractor |
goldens/size-tracking/*.json |
| Integration | pnpm bazel test //integration/... |
integration/<scenario> |
| DevTools E2E | Cypress | devtools/cypress |
| Performance | Benchpress | modules/benchmarks, modules/ssr-benchmarks |
| Tsec / trusted types | bazelisk test with --build_tag_filters=tsec |
pnpm test-tsec |
Daily commands
# Targeted run during iteration
pnpm bazel test //packages/core/test:test
# Watch mode for one target
pnpm bazel test //packages/core/test:test --test_arg=--watch
# Whole-repo run minus the slow suites
pnpm test:cipnpm test:ci excludes integration, adev, devtools, vscode language service, and SSR benchmarks. CI runs all of those separately.
The zoneless / Act-Wait-Assert pattern
AGENTS.md codifies the testing pattern for new tests in a zoneless world:
it('should reflect updated value', async () => {
const fixture = TestBed.createComponent(MyComponent);
// Act
fixture.componentInstance.text.set('hello');
// Wait
await fixture.whenStable();
// Assert
expect(fixture.nativeElement.textContent).toContain('hello');
});Rules:
- Do not call
fixture.detectChanges()to push updates. Signals schedule asynchronously; manualdetectChanges()masks ordering bugs. - Use real async tests —
it('...', async () => { ... })— andawait fixture.whenStable()between mutation and assertion. - For tests that depend on time, prefer
useAutoTick()frompackages/private/testing/src/utils.tsto fast-forward the mock clock. - Use
await timeout(ms)from the same utils when you genuinely need to wait wall-clock time.
This pattern is enforced by review for any new test. Existing tests that still call detectChanges() are migrated opportunistically.
TestBed configuration
Standalone components mean most tests no longer need a per-test NgModule:
TestBed.configureTestingModule({
imports: [MyStandaloneComponent],
providers: [provideRouter([]), provideHttpClient()],
});For environment-level providers (provideRouter, provideHttpClient, etc.), pass them via providers. The injector is environment-scoped automatically.
Browser vs node tests
- Karma (browser) runs in the browser. Tests that touch the DOM, hydration, or
platform-browsergo here. nodejs_testruns in pure Node. Tests for the compiler, language service, and any non-DOM logic go here.
The Bazel rule used by a given test target encodes the runner choice. Look at the BUILD.bazel next to the test source.
Public API and bundle size goldens
Public API surface is asserted via TypeScript declarations in goldens/public-api/. Adding a new export requires regenerating:
pnpm public-api:updateBundle-size budgets are tracked in goldens/size-tracking/. Run:
pnpm symbol-extractor:updateafter intentional bundle changes. CI fails if either snapshot differs from HEAD.
Integration tests
integration/ contains end-to-end scenarios that consume the built @angular/* packages, e.g.:
cli-hello-world,cli-hello-world-lazy— the smallest possible CLI consumer apps.cli-signal-inputs— verifies signal input migration scenarios.platform-server,platform-server-hydration— SSR with and without hydration.defer—@deferblock runtime verification.ng_elements—@angular/elementsintegration.
Run:
pnpm integration-tests:ciEach scenario has its own package.json. They install with pnpm against the locally built @angular/* artifacts.
DevTools E2E
Cypress runs against a development build of the DevTools panel:
pnpm devtools:devserver # In one terminal
pnpm devtools:e2e:open # Cypress UI
# or
pnpm devtools:test:e2e # HeadlessTest fixtures live in devtools/cypress/. The harness loads sample apps in an iframe and asserts the DevTools panel renders the expected component tree.
Tsec / trusted types
tsec is a TypeScript security linter that enforces Trusted Types contracts on the framework itself (the runtime is responsible for not introducing DOM-injection sinks). It runs via:
pnpm test-tsecExemptions live in packages/tsec-exemption.json.
Benchmarks
Run the local benchmark harness:
pnpm benchmarksApps under modules/benchmarks exercise component creation, change detection, hydration, and templated tables. The CI workflow benchmark-compare.yml runs the same suite against HEAD vs the merge base and posts a perf delta on PRs labeled requires: TGP.
Debugging a failing test
- Add
fdescribe/fitto focus a single test (Jasmine). - Insert a
debugger;statement and run with the Bazel--config=debugflag plus a Node inspector for node tests. - For Karma, the
--karma_debugflag runs the browser with DevTools open.
The full debugging walkthrough is in debugging and contributing-docs/debugging-tips.md.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.