How to Test Angular Components with Cypress
Set up Cypress Component Testing for Angular, mount components, provide inputs and dependencies, and test browser-visible behavior with practical examples.
Cypress Component Testing lets you mount an Angular component in a real browser, interact with its rendered DOM, and assert the results a user can see. Install Cypress, select Component Testing in the Cypress app, let it configure the Angular project, then write a spec that calls cy.mount(). Before setup, check the current Cypress compatibility page: its Angular overview currently lists Angular ^21.0.0 and ^22.0.0, and requires @angular-devkit/build-angular for its Angular harness, including projects using @angular/build. Compatibility changes, so verify the official page for your versions.
1. Check compatibility and install Cypress
Cypress Component Testing starts a development server to compile and serve component specs. It does not visit your deployed production or staging application. Cypress’s setup flow can detect an Angular CLI project and configure component testing.
Install Cypress using your project’s package manager. For npm:
npm install --save-dev cypress
For pnpm or Yarn, use the equivalent development dependency command:
pnpm add --save-dev cypress
# or
yarn add --dev cypress
Open the Cypress app and choose Component Testing. Follow its prompts to choose the framework and bundler configuration. If the project is missing the Angular build package required by the harness, add @angular-devkit/build-angular at a version compatible with the project’s Angular CLI setup.
Starting with Cypress 16, the Angular harness supports zoneless testing without additional configuration or zone.js. The Cypress overview describes zoneless as the default for Angular 21 and 22. Older Angular versions may have different support; do not assume compatibility from this guide.
2. Write and run a first component spec
Suppose the project has a StepperComponent. Create a component spec next to it, following the project’s naming convention, and mount the component:
import { StepperComponent } from './stepper.component'
describe('StepperComponent', () => {
it('mounts', () => {
cy.mount(StepperComponent)
})
})
Run Cypress Component Testing from the app and select the spec. The test mounts the component in the browser. Add selectors, actions and assertions that represent observable behavior, rather than stopping at a mount-only test.
3. Test visible behavior and interaction
Here is a small standalone component with a button and a displayed count:
import { Component } from '@angular/core'
@Component({
selector: 'app-stepper',
standalone: true,
template: `
<button type="button" (click)="count++">Increment</button>
<output aria-label="Count">{{ count }}</output>
`,
})
export class StepperComponent {
count = 0
}
Test what a user can see and do:
import { StepperComponent } from './stepper.component'
describe('StepperComponent', () => {
it('increments the displayed count when clicked', () => {
cy.mount(StepperComponent)
cy.get('output[aria-label="Count"]').should('have.text', '0')
cy.contains('button', 'Increment').click()
cy.get('output[aria-label="Count"]').should('have.text', '1')
})
})
Prefer accessible, stable selectors such as roles, labels and visible text when they identify the behavior well. If a selector based on styling or DOM structure is necessary, keep it narrowly scoped so harmless markup changes do not break the spec.
4. Provide inputs, providers and imports
Pass initial component values with componentProperties. Mount options can also supply providers, declarations and imports when the component needs dependencies. A standalone component often carries its imports in its own metadata, so it may mount directly; do not duplicate a universal fixture setup for every Angular project style.
Example for a component with a legacy input and an output:
import { Component, EventEmitter, Input, Output } from '@angular/core'
@Component({
selector: 'app-greeting',
standalone: true,
template: `
<p>Hello, {{ name }}</p>
<button type="button" (click)="saved.emit(name)">Save</button>
`,
})
export class GreetingComponent {
@Input() name = 'Guest'
@Output() saved = new EventEmitter<string>()
}
import { GreetingComponent } from './greeting.component'
describe('GreetingComponent', () => {
it('renders its input and emits it when saved', () => {
const onSaved = cy.spy().as('onSaved')
cy.mount(GreetingComponent, {
componentProperties: {
name: 'Avery',
saved: onSaved,
},
})
cy.contains('Hello, Avery').should('be.visible')
cy.contains('button', 'Save').click()
cy.get('@onSaved').should('have.been.calledOnceWith', 'Avery')
})
})
For legacy @Input() values that need to change after mounting, update through Angular’s component reference so change detection handles the input correctly:
cy.mount(GreetingComponent).then(({ fixture }) => {
fixture.componentRef.setInput('name', 'Jordan')
fixture.detectChanges()
})
cy.contains('Hello, Jordan').should('be.visible')
You can pass a Cypress spy for an output as above, or use Cypress Angular’s createOutputSpy() helper and assert that the spy received the expected value. For signal inputs and model signals, plain values work as initial values. Use writable signals when the test must change a bound value after the component has mounted. Check Cypress’s current Angular examples for the exact helper imports supported by your installed version.
Adding a provider for a dependency
For a component that injects a service, provide the test double in the mount options. This illustrates the shape; adapt the token and methods to the component:
const accountService = {
displayName: () => 'Taylor',
}
cy.mount(ProfileComponent, {
providers: [
{ provide: AccountService, useValue: accountService },
],
})
cy.contains('Taylor').should('be.visible')
Use the project’s real service when the integration with that service is the behavior under test. Use a focused stub when you want to isolate component rendering or interaction from network and application state.
5. Decide which test layer fits the behavior
| Test layer | Environment and scope | Good fit |
|---|---|---|
| Class-only unit test | Tests logic without mounting a DOM component | Calculations, formatting, or behavior that does not depend on rendering |
| Cypress component test | Mounts one component in a real browser | Rendering, user interaction, and component behavior that depends on the browser DOM |
| End-to-end test | Exercises a larger application flow | Integration across routes, application services, and multiple parts of the running app |
Angular describes a component as its class working together with its template. DOM tests help verify rendering, response to user input, and integration with parent and child components; class-only tests can cover behavior that needs no DOM. Cypress component tests add browser rendering and interaction coverage, but they do not replace every unit or end-to-end test.
6. Troubleshooting common problems
| Symptom | Likely cause | Fix |
|---|---|---|
| Cypress does not offer or start Angular component testing | Unsupported Angular version, missing Angular CLI setup, or missing @angular-devkit/build-angular |
Check the current Cypress Angular compatibility page and install the required build package at a compatible version. |
| A component fails with a missing provider error | The component injects a service or token that the test did not provide | Add the provider to mount options, or import the module/configuration that provides it. Use a test double if the dependency is not part of the behavior being tested. |
| An imported directive or pipe is unknown | A non-standalone component’s module dependencies are absent from the test mount setup | Add the needed declarations and imports in mount options. For a standalone component, check that its own component metadata includes the required imports. |
| An input update does not appear in the DOM | The test changed an instance property directly and Angular did not process it as an input update | For legacy inputs, use fixture.componentRef.setInput() and run change detection as needed. |
| An output assertion never fires | The output spy was not passed as the component property, or the test did not trigger the emitting action | Pass the spy for the output when mounting, interact with the relevant control, and assert the expected emitted argument. |
| A test works alone but fails in the suite | It may rely on state shared with another spec or on an assumption about asynchronous rendering | Give each spec its own mount and fresh dependency state. Assert a visible condition instead of relying on an arbitrary delay. |
| A component that uses browser APIs fails during mount | The API may not exist in the test browser context or may need setup before component code runs | Stub or provide the browser API in the test setup appropriate to the project, and keep the stub scoped to the test. |
7. Performance, reliability and cost
Component testing runs a browser and a development server, so it has more setup and runtime overhead than a class-only test. Keep component specs focused on meaningful DOM behavior; cover pure calculations at the class or function level when rendering adds no value. Reuse a small, consistent set of mount helpers for common providers, while keeping test-specific dependencies explicit.
For reliability, use accessible selectors, fresh component state per spec, deterministic service responses, and assertions tied to observable outcomes. Avoid fixed waits when a DOM condition can express readiness. A component test is not evidence that the deployed application’s complete routing and backend flow works; use an end-to-end test for those integrated paths.
Cypress’s cited documentation does not provide a universal speed, coverage, or defect-reduction figure for this workflow. Runtime and infrastructure cost depend on the project and how the tests are run; measure in your own CI before setting budgets.
8. Or skip the browser setup
For website screenshots, ScreenshotNeo is a website screenshot API and MCP server for developers. It is not an Angular component test runner: use Cypress to test component behavior. If your workflow also needs screenshots of pages, a single GET request can return an image or PDF. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
ScreenshotNeo accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each step can be turned off. Bot checks, blank pages and failed loads are never billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Sign up for 1,000 free screenshots a month, with no card.
FAQ
Does Cypress component testing run against my deployed site?
No. It compiles and serves the component spec through a development server. Use end-to-end tests for deployed or fully integrated application flows.
Can I mount a standalone Angular component directly?
Often, yes. Standalone components declare their imports in component metadata. Add mount options when the component still needs test-specific providers or other setup.
Do component tests replace Angular unit tests?
No. Use class-only tests for logic that does not need a DOM, component tests for rendered browser behavior, and end-to-end tests for wider application flows.


