How to Test Ionic Mobile Apps with Cypress
Use Cypress to test Ionic’s browser-rendered UI, responsive layouts, and user journeys. Learn where browser tests stop and native device tests begin.
Cypress can test the browser-rendered part of an Ionic app: launch the app in a browser, exercise user journeys, and check how the interface responds at mobile viewport sizes. It does not run a shipped native iOS or Android app. If behavior depends on a native plugin, device hardware, or platform runtime, add tests that run the native app on the relevant platform.
The practical workflow is to serve the Ionic app, configure Cypress with its local URL, set a mobile-sized viewport, and write predictable end-to-end tests. Cypress can also mount individual components for focused browser tests. Both approaches test web behavior, not native execution. Cypress’s FAQ explains the boundary, while Ionic’s documentation describes the framework and its browser development workflow.
1. Set up Cypress for an Ionic app
Start the Ionic development server
From the project directory, start the app with Ionic CLI:
ionic serve
ionic serve starts a local development server and watches files for changes. Use the CLI options appropriate for your project when you need a particular host, browser, HTTPS setup, or Ionic configuration; see the ionic serve command reference. Keep the server available while Cypress runs.
Install and open Cypress
Add Cypress as a development dependency using the package manager your project already uses:
npm install --save-dev cypress
npx cypress open
The Cypress Launchpad guides you through setting up end-to-end or component testing. The examples below use end-to-end testing against the running Ionic app. Follow the current Cypress installation guide if your project uses a different package manager or needs a specific setup.
Set the base URL
In current Cypress projects, configure the app URL in cypress.config.js (or the equivalent TypeScript configuration file). Replace the example port if your Ionic server prints a different local address:
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:8100',
specPattern: 'cypress/e2e/**/*.cy.js'
}
});
The Ionic/Cypress tutorial published in 2020 remains useful for its example journeys, but its older cypress.json configuration should not be copied as current setup. Use the configuration format supported by your installed Cypress release.
2. Write a browser end-to-end test
Create cypress/e2e/home.cy.js. The selectors below are examples: match them to your app’s actual markup. Adding stable data-cy attributes to important controls makes tests less dependent on styling and copy.
describe('Ionic app in a mobile-sized browser', () => {
beforeEach(() => {
cy.viewport(390, 844);
cy.visit('/');
});
it('shows the main screen and completes a key journey', () => {
cy.get('[data-cy=home-title]').should('be.visible');
cy.get('[data-cy=search]').type('mobility');
cy.get('[data-cy=search-result]').should('be.visible');
cy.get('[data-cy=favorite-button]').click();
cy.get('[data-cy=favorite-button]').should('have.attr', 'aria-pressed', 'true');
});
});
Run npx cypress open to select and run the spec interactively, or use npx cypress run for a headless run. Cypress’s end-to-end testing guidance covers the visit-and-assert workflow.
Use selectors and assertions that reflect user outcomes
Prefer stable selectors such as data-cy for controls that do not have useful accessible names. Where possible, query by role or visible label so the test also reflects how users find controls. After an action, assert its result: a route changed, a result appeared, a favorite state toggled, or a theme class was applied. A successful click alone does not establish that the journey worked.
3. Check mobile-sized layouts with Cypress viewports
A viewport changes the browser’s available width and height. It is useful for responsive breakpoints, layout overflow, and visibility checks. It does not make Cypress run on a phone or reproduce a native WebView.
Set a default size in Cypress configuration if most tests share one, and override it in tests that cover other breakpoints:
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:8100',
viewportWidth: 390,
viewportHeight: 844
}
});
it('keeps the navigation available on a narrow layout', () => {
cy.viewport(375, 812);
cy.visit('/');
cy.get('[data-cy=menu-button]').should('be.visible');
cy.get('body').should(($body) => {
expect($body[0].scrollWidth).to.be.at.most($body[0].clientWidth);
});
});
Cypress also accepts named device presets and orientation settings. Presets are convenient viewport definitions, not a device emulator. Choose dimensions that correspond to the breakpoints and layouts your app supports, and cover a small, representative set rather than assuming one preset proves all mobile behavior. See the cy.viewport() API for supported forms and options.
4. Make app state deterministic
Tests are easier to diagnose when every run starts from known state. Onboarding flags, local storage, authentication, and server data can otherwise make the same test behave differently between runs.
Use the state-control mechanism that fits your app. For a simple local-storage flag, Cypress can set it before visiting:
beforeEach(() => {
cy.viewport(390, 844);
cy.visit('/', {
onBeforeLoad(win) {
win.localStorage.setItem('onboardingComplete', 'true');
}
});
});
This key is only an example; use the key and state model your app actually reads. If Ionic Storage or another asynchronous storage layer is involved, initialize it through app-supported test setup or a controlled test route rather than assuming a local-storage write is enough. Keep tests isolated: reset or seed data as needed, and avoid depending on a previous test’s clicks.
5. Test Ionic gestures in the browser
Cypress can exercise a browser-side interaction path by dispatching mouse events. The Ionic/Cypress example uses this technique for a slide gesture. It simulates events in the page; it is not native touch injection and does not prove that a device gesture recognizer works.
A project-specific command can trigger the events against the active slide:
// cypress/support/commands.js
Cypress.Commands.add('dragSlideLeft', (selector) => {
cy.get(selector).trigger('mousedown', { which: 1, pageX: 300, pageY: 100 });
cy.get(selector).trigger('mousemove', { which: 1, pageX: 80, pageY: 100 });
cy.get(selector).trigger('mouseup', { which: 1, pageX: 80, pageY: 100 });
});
// In a spec
cy.dragSlideLeft('[data-cy=active-slide]');
cy.get('[data-cy=next-slide]').should('be.visible');
Adapt the target and event coordinates to the component and gesture logic in your app. Some gesture implementations listen for pointer or touch events instead of mouse events; a mouse-event test will not cover those handlers. For actual touch behavior, validate on the native app and target device stack.
6. Choose end-to-end or component testing
| Test type | What it covers | Use it for | What it does not establish |
|---|---|---|---|
| Cypress end-to-end | The running app in a browser, across navigation and integrated UI | Important user journeys, routing, and app-level browser behavior | Native plugin or device-runtime behavior |
| Cypress component | A mounted component in a real browser, in isolation | Component states, interactions, and focused feedback | That full-app navigation and integration work |
| Native app/device tests | The packaged app and its platform runtime, using a native testing stack | Plugin integration, hardware, platform-specific behavior, and native touch | Every browser layout and user journey unless those are also covered |
Cypress supports browser-based E2E and component testing. Its component testing documentation describes framework configuration. For native execution, Ionic’s E2E testing reference example discusses a separate approach, including WebdriverIO and Appium.
7. Know what Cypress cannot verify
Cypress tests the browser-rendered web application. It can check mobile-sized presentation and browser UI interactions, but it does not execute a shipped native iOS or Android app. Native functionality needs appropriate native execution and device or platform coverage.
- Good Cypress candidates: responsive layout, browser navigation, form flows, content visibility, web-side state changes, and browser-rendered Ionic components.
- Needs native coverage: camera access, fingerprint authentication, native plugin integration, real touch recognition, platform permissions, and platform runtime behavior.
- Use both when needed: a browser test can verify the web journey while a native test verifies the plugin or device interaction that the browser cannot execute.
Cypress’s FAQ states that it cannot run on a native mobile app while it can test mobile web browsers and browser-based mobile applications such as Ionic. Treat viewport emulation and event simulation as browser checks, not device certification.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
cy.visit('/') cannot connect |
The Ionic server is stopped, running on another port, or bound to a different host. | Start ionic serve, check its printed address, and align Cypress baseUrl with it. |
| The app works locally but a spec sees a blank or loading page | The test visits before app initialization finishes, or a required service/state is unavailable. | Wait for a meaningful app selector with a bounded timeout; seed required state and make test dependencies available. |
| Tests pass alone but fail in a suite | State leaks between tests or tests rely on execution order. | Reset or seed state per test, avoid shared mutable fixtures, and assert each test’s starting condition. |
| A selector breaks after a visual redesign | The test depends on CSS classes, DOM structure, or text that changed. | Add stable data-cy hooks or query by accessible role and name where appropriate. |
| A swipe test does nothing | The component listens for touch or pointer events, or the dispatched mouse sequence does not match its gesture logic. | Match the browser event path the component uses; use native device testing for real touch behavior. |
| A viewport test is mistaken for a phone test | Viewport dimensions are being treated as hardware or native-runtime emulation. | Use Cypress for responsive browser behavior and add native execution for device-dependent behavior. |
| Stored onboarding state is ignored | The app reads a different key or uses an asynchronous storage layer. | Use the app’s real test setup path and confirm when initialization reads the state. |
9. Performance, reliability, and cost
Browser E2E tests run the application and its dependencies, so they cover more integration than isolated component tests and usually need more setup. Use component tests for numerous focused states and reserve E2E tests for representative critical journeys. Keep state setup explicit, use stable selectors, and wait on meaningful application conditions rather than arbitrary delays to make failures easier to understand.
The dossier does not establish universal runtime benchmarks, device coverage, or a fixed cost for a Cypress setup. Actual time and cost depend on your app, CI environment, and any native test infrastructure you add. A viewport test can reduce the need to manually inspect every responsive state, but it does not replace native runs for device-specific behavior.
Or skip the browser setup
If your immediate task is to capture the rendered Ionic page for review or documentation, ScreenshotNeo can return a screenshot or PDF from one API request. It does not replace Cypress assertions or native tests. Its API can capture a URL without installing and driving a browser in your own script:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://ionicframework.com -o shot.webp
See the ScreenshotNeo API documentation for request options. ScreenshotNeo removes cookie banners, popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000 screenshots. Sign up for the free plan.
FAQ
Can Cypress test native mobile apps?
No. Cypress runs browser tests. Use a native testing stack for a packaged iOS or Android app and its platform-specific behavior.
Can Cypress test Ionic at a mobile screen size?
Yes. Set a viewport with cy.viewport() or Cypress configuration to check responsive browser layouts. That does not run the test on a physical phone.
Can Cypress test swipes in Ionic?
It can simulate browser events with a custom command to exercise a web interaction path. That does not certify native touch handling.
Should I use component or end-to-end tests?
Use component tests for isolated browser states and E2E tests for integrated journeys through the running app. Add native tests when the behavior depends on a device or native runtime.


