Skip to main content


Testing is a deep topic, involving a wide variety of testing approaches at different levels of an app. We visualize testing as a pyramid, and we'll explore each layer in this guide.

Testing Pyramid

Where to start

Start and the bottom and work up. That is, if static analysis is not yet implemented, start there by implementing linting with each commit, disallowing commits that do not pass. Then move up to unit tests, etc.

The area of each area is a rough estimate of both the value of the tests as well as the overall volume of the tests.

Moving up the pyramid increases the following items: cost, complexity, difficulty of maintenance / fragility.

Static Analysis and Linting

This layer refers to the use of linting tools or static analysis tools like Sonarqube to analyze the code and look for common issues and violations of best practices. Especially in the case of tools like linters it is very easy to run the tool with every commit. 100% coverage of the code should be very easy to achieve.

There are a few widely used linters but we recommend only one: ESLint. ESLint is the defacto standard for linting tools in the JavaScript world. For developers on Angular where TSLint is still used, the Angular docs recommend not using it and instead to follow the instructions to enable the community ESLint functionality.

Unit Tests

Unit tests focus on testing individual components, page, service, and other code modules in isolation ensuring that the module under test meets the requirements for the module. Key aspects of unit tests:

  • individual code modules are tested in isolation, all dependencies are mocked, so if component A is being tested and it uses service B, service B is mocked
  • tests are developed to determine where the module does not meet the requirements
  • 100% coverage of the requirements is the target, often leading to 100% code coverage

Unit tests are generally run continuously during development, as well as with each CI/CD build.

In Angular, testing is built right into the official toolchain. Follow the Angular Testing docs to learn more. In React, testing may or may not be built into your project depending on whether it used create-react-app or something else. Follow the React Testing docs to learn more.

Integration Tests

Integration tests are technically similar to unit tests, often using the same tools and libraries with the key difference being that only external dependencies are mocked. So if component A is being tested and it uses service B, service B is not mocked like it would be for a unit test. However, if service B makes HTTP calls to an API that is an external dependency and those HTTP calls are mocked.

Integration testing focuses on the interaction between modules rather than requirements or code coverage.

End-to-end Testing

End-to-end testing exercises the application as a whole as opposed to individual modules. It is difficult to achieve full coverage of requirements or code.

End-to-end testing focuses on various user facing scenarios and workflows. For example:

  • logging in to the app
  • opening your transaction list
  • entering a new transaction

With most Ionic Framework applications, automated end-to-end testing is performed using popular web-based testing tools such as Cypress or Selenium in order to exercise the application. The majority of a typical Ionic app is web-based, so these tools work well for testing them.

Device Testing

Automated device testing generally is performed by running end-to-end tests on a device farms. Automated device testing should focus on scenarios that are focused on native interactions. Some popular services here are AWS Device Farm, Sauce Labs, and Browserstack.

Manual Testing

No matter how much automated testing is in place, there is no replacement for getting the application into the hands of human beings to run the application. Common forms of manual testing include QA regression testing prior to release, and acceptance testing performed by key consumers of the application.