Skip to main content

Testing Ionic Applications

1. Setting Up Testing Environment

Example: Install testing dependencies

npm install --save-dev @ionic/angular-test @testing-library/angular jest jest-environment-jsdom
Dependency Description Version
@ionic/angular-test Official testing utilities for Ionic Angular apps NEW 7.2.0
@testing-library/angular Angular testing utilities for DOM interactions 15.0.0
jest JavaScript testing framework 29.5.0
jest-environment-jsdom JS DOM environment for Jest tests 29.5.0
Note: Ensure your tsconfig.spec.json includes the necessary test configurations.

Example: Configure Jest for Ionic

// jest.config.js
module.exports = {
  testEnvironment: 'jest-environment-jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/test.ts'],
  moduleNameMapper: {
    '^src/(.*)

2. Writing Unit Tests

Example: Basic Unit Test for Ionic Component

import { ComponentFixture, TestBed } from &#x27;@angular/core/testing&#x27;;
import { IonicModule } from &#x27;ionic-angular&#x27;;
import { MyComponent } from &#x27;./my-component&#x27;;

describe(&#x27;MyComponent&#x27;, () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [MyComponent],
      imports: [IonicModule.forRoot()]
    }).compileComponents();
  });

  it(&#x27;should create&#x27;, () => {
    expect(component).toBeTruthy();
  });
});
Test Type Description Framework Notes
Unit Test Tests individual components or functions in isolation Jest, Jasmine Use Angular TestBed for Ionic components
Integration Test Tests interaction between components Jest, Jasmine Use TestBed with multiple components
End-to-End Test Tests full application flow in a real browser Cypress, Protractor Simulates user interactions
Note: Ionic components often require IonicModule.forRoot() in test configurations to render correctly.
Warning: Avoid testing DOM manipulation directly; use Angular's testing utilities instead.

3. Testing Components

Example: Testing a Button Component

import { ComponentFixture, TestBed } from &#x27;@angular/core/testing&#x27;;
import { IonicModule } from &#x27;ionic-angular&#x27;;
import { MyButtonComponent } from &#x27;./my-button.component&#x27;;

describe(&#x27;MyButtonComponent&#x27;, () => {
  let component: MyButtonComponent;
  let fixture: ComponentFixture<MyButtonComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [MyButtonComponent],
      imports: [IonicModule.forRoot()]
    }).compileComponents();

    fixture = TestBed.createComponent(MyButtonComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it(&#x27;should create&#x27;, () => {
    expect(component).toBeTruthy();
  });

  it(&#x27;should emit click event&#x27;, () => {
    spyOn(component.clickEvent, &#x27;emit&#x27;);
    component.onClick();
    expect(component.clickEvent.emit).toHaveBeenCalled();
  });
});
Property Description Default Value Notes
text Text content of the button '' Required
type Button type (button, submit, reset) 'button' Valid values: button, submit, reset
disabled Disables the button if true false Boolean flag
Note: Use TestBed to configure the testing module and create component instances.
Warning: Ensure all dependencies are properly mocked or provided in the test module.

Testing Component Workflow

  • Import necessary testing modules and components
  • Configure the test module with TestBed
  • Create component instance using TestBed.createComponent()
  • Run tests using Jasmine or Jest syntax

4. Mocking Services

Service Mocking Approach Notes
HttpService HttpClientTestingModule Use Angular's testing module for HTTP requests
StorageService Storage mock with jest.fn() Mock storage methods like get, set
UserService Manual mock with spyOn Mock user data and auth logic

Example: Mocking HttpService

import { HttpClientTestingModule } from &#039;@angular/common/http/testing&#039;;
import { TestBed } from &#039;@angular/core/testing&#039;;

describe(&#039;MyComponent&#039;, () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [MyService]
    });
  });

  it(&#039;should fetch data&#039;, () => {
    const service = TestBed.inject(MyService);
    const httpSpy = spyOn(service.http, &#039;get&#039;).and.returnValue(of({ data: &#039;mocked&#039; }));
    service.fetchData().subscribe(res => {
      expect(res).toEqual({ data: &#039;mocked&#039; });
    });
  });
});

Example: Mocking StorageService

import { Storage } from &#039;@ionic/storage-angular&#039;;

const mockStorage = {
  get: jest.fn(),
  set: jest.fn()
};

describe(&#039;MyComponent&#039;, () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        { provide: Storage, useValue: mockStorage }
      ]
    });
  });

  it(&#039;should use mocked storage&#039;, () => {
    const service = TestBed.inject(MyService);
    mockStorage.get.mockResolvedValue(&#039;mockedValue&#039;);
    service.getData().then(res => {
      expect(res).toBe(&#039;mockedValue&#039;);
    });
  });
});

5. Testing Navigation

Example: Testing Navigation with Ionic

import { TestBed } from &#x27;@angular/core/testing&#x27;;
import { RouterTestingModule } from &#x27;@angular/router/testing&#x27;;
import { IonicModule } from &#x27;ionic-angular&#x27;;
import { MyApp } from &#x27;./app.component&#x27;;

describe(&#x27;MyApp&#x27;, () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [MyApp],
      imports: [
        IonicModule.forRoot(MyApp),
        RouterTestingModule
      ]
    });
  });

  it(&#x27;should navigate to home page&#x27;, () => {
    const fixture = TestBed.createComponent(MyApp);
    const app = fixture.debugElement.componentInstance;
    app.nav.push(HomePage);
    expect(app.nav.getActive().component).toBe(HomePage);
  });
});
Method Description Parameters Return Type
nav.push() Navigates to a new page component, params Promise<any>
nav.pop() Pops the current page off the stack options Promise<any>
nav.setRoot() Sets the root page of the app component, params Promise<any>
nav.getStack() Gets the navigation stack none Array<any>
Note: Use RouterTestingModule to mock navigation in unit tests.
Warning: Ensure that navigation logic is tested in isolation to avoid side effects.

6. Writing E2E Tests

E2E (End-to-End) tests in Ionic applications ensure that the app behaves as expected from user interaction to backend responses. These tests are typically written using tools like Playwright or Cypress.

Test Framework Features Notes
Playwright supports web, mobile, and desktop apps Good for cross-platform testing with rich API
Cypress fast, easy to debug Great for web apps and component testing

Example: Playwright E2E Test Setup

import { test, expect } from '@playwright/test';

test('should display welcome message', async ({ page }) => {
  await page.goto('/');
  const heading = page.locator('h1');
  await expect(heading).toHaveText('Welcome to Ionic!');
});

Example: Cypress E2E Test Setup

describe('Ionic App E2E Tests', () => {
  it('should show welcome message', () => {
    cy.visit('/');
    cy.get('h1').contains('Welcome to Ionic!');
  });
});
Note: E2E tests should be run in a CI/CD pipeline to ensure app stability across environments.

7. Testing User Interactions

Example: Simulating Tap Events

import { ComponentFixture } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

it('should handle tap on button', () => {
  const fixture = TestBed.createComponent(MyComponent);
  const button = fixture.debugElement.query(By.css('ion-button'));
  button.triggerEventHandler('click', null);
  fixture.detectChanges();
  expect(component.buttonClicked).toBeTrue();
});
Event Description Testing Method
tap Simulates a tap interaction on an element triggerEventHandler('click', null)
swipe Simulates swipe gesture on a component Use UIEvent or mock touch events
Note: For mobile-specific interactions, consider using ion-content and ion-scroll event simulations.

Example: Testing Scroll Events

it('should handle scroll event', () => {
  const fixture = TestBed.createComponent(MyScrollComponent);
  const scrollElement = fixture.debugElement.query(By.css('.scroll-container'));
  const scrollEvent = new UIEvent('scroll');
  scrollElement.triggerEventHandler('scroll', scrollEvent);
  fixture.detectChanges();
  expect(component.scrollPosition).toBe(100);
});

8. Implementing Visual Regression Testing

Example: Setting up Percy for Ionic

// percy.config.js
module.exports = {
  snapshot: {
    widths: [375, 768, 1024],
    minHeight: 1024
  },
  upload: {
    asset: {
      ignore: ['**/node_modules/**']
    }
  }
}
Feature Description Usage
percy snapshot Command to capture visual snapshots percy snapshot src/**/*
percy exec Wrapper to run tests with Percy percy exec npm test
Note: Percy requires a Percy token for uploading snapshots to the cloud.

Visual Regression Tools Comparison

  • Percy: Cloud-based, easy to integrate with CI
  • Selenium: Self-hosted, more control over browser behavior
  • Playwright: Modern, fast, built-in screenshot support

Setup Workflow

  1. Install Percy CLI: npm install -g @percy/cli
  2. Set up Percy project in dashboard
  3. Add Percy to test runner configuration
  4. Run tests with visual snapshots

9. Testing Platform-Specific Code

Testing platform-specific code in Ionic applications requires understanding how to handle device-specific APIs and conditional logic. Below are key approaches and considerations.

Feature Description Testing Approach
Device APIs Access to native device features like camera, GPS, etc. Use cordova-plugin-xxx or @capacitor/xxx in tests with mocks or stubs.
Platform Detection Detecting the current platform (iOS, Android, Web) Use Platform service from @ionic/angular or @capacitor/core.
Conditional Logic Executing code based on platform Wrap platform-specific logic in conditional blocks and test each branch separately.

Example: Platform Detection in Tests

import { Platform } from &#x27;@ionic/angular&#x27;;

describe(&#x27;Platform-specific logic&#x27;, () => {
  let platform: Platform;

  beforeEach(() => {
    platform = new Platform();
  });

  it(&#x27;should run web logic on web platform&#x27;, () => {
    spyOn(platform, &#x27;is&#x27;).and.returnValue(false);
    // Test web-specific logic here
  });

  it(&#x27;should run mobile logic on mobile platform&#x27;, () => {
    spyOn(platform, &#x27;is&#x27;).and.returnValue(true);
    // Test mobile-specific logic here
  });
});
Note: When testing platform-specific code, ensure that your tests simulate the correct environment (e.g., mobile vs web) to accurately reflect runtime behavior.

10. Running Tests

Example: Running Ionic tests with Jest

npm run test
Command Description Flags
npm run test Run all tests in the project --watch, --coverage
npm run test -- --watch Run tests in watch mode
npm run test -- --coverage Generate test coverage report
Note: Ensure Jest is configured in your Ionic project for effective test running.

11. Measuring Code Coverage

Feature Description Command Notes
istanbul Code coverage tool for JavaScript/TypeScript npx istanbul cover _mocha Used with Mocha for test coverage
nyc Next generation Istanbul code coverage tool npx nyc mocha Alternative to Istanbul with better performance
jest JavaScript testing framework with coverage support jest --coverage Built-in coverage reporting

Example: Setting up Jest with Coverage

{
  "scripts": {
    "test": "jest --coverage"
  },
  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{ts,js}",
      "!src/**/*.d.ts"
    ]
  }
}
Note: Jest generates coverage reports in coverage/lcov-report/index.html
: '<rootDir>/src/$1', }, collectCoverageFrom: [ 'src/**/*.{ts}', '!src/main.ts', '!src/polyfills.ts' ] }

Was this article helpful?