The Angular CLI includes support for Unit and End-to-End Testing using tools and technologies that are known to work well.
However, if you use Ionic's CLI to create the scaffolding for a new project you'll notice that it doesn't include any support for Unit or End-to-End Testing.
The Angular CLI
In a previous post, I used the Angular CLI to create the scaffolding for a new application:
ng new angular-wijmo-flexsheet
The Angular CLI creates a placeholder package.json
file:
"devDependencies": {
...
"jasmine-core": "~2.6.2",
"jasmine-spec-reporter": "~4.1.0",
"karma": "~1.7.0",
"karma-chrome-launcher": "~2.1.1",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
...
}
That includes Jasmine, Karma and Protractor as 'devDependencies'.
As well as test
and e2e
tasks:
"scripts": {
...
"test": "ng test",
"e2e": "ng e2e",
...
},
The Angular CLI also creates a placeholder karma.conf.js
file and a placeholder protractor.conf.js
file in the project's root directory.
As well as some additional testing-related files in the /src
directory:
- test.ts - Responsible for loading the project's specs.
- tsconfig.app.json - The Angular CLI's TypeScript config file.
- tsconfig.spec.json - The Angular CLI's TypeScript spec config file.
Note: Tests written in Jasmine are called specs. The filename extension must be .spec.ts
, the convention adhered to by karma.conf.js
and other tools.
Looking at a project created using the Angular CLI means we now have a much better idea as to how we might go about adding support for Unit and End-to-End Testing to an existing Ionic project.
What problem are we solving?
As your project evolves unit and end-to-end testing reduces the likelihood of introducing breaking changes when implementing new features and bugfixes.
Unit and End-to-End Testing
If you have an existing Ionic project and you'd like to use the tools and technologies supported by the Angular CLI, you can follow the steps in this post and the associated project.
It's the approach I followed when adding support for unit and end-to-end testing to the Big Top App:
├── /big-top
└── /config
├── karma.conf.js - Karma's config file
├── protractor.conf.js - Protractor's config file
└── /coverage
└── /src
├── polyfills.ts - Pollyfills used by the Angular CLI
├── test.ts - Test driver
├── tsconfig.spec.json - Angular's CLI TypeScript spec config file
└── /www
├── .angular-cli.json - Angular's CLI config file
├── tsconfig.ng-cli.json - Angular's CLI base compiler config file
├── tsconfig.json - TypeScript's configuration file
├── package.json
Note: I've only included the directories and files discussed in this post.
The updated package.json:
"scripts": {
...
"test": "ng test --config ./config/karma.conf.js",
"test-ci": "ng test --config ./config/karma.conf.js --watch=false --code-coverage",
"test-coverage": "ng test --config ./config/karma.conf.js --code-coverage",
"e2e": "npm run e2e-update && npm run e2e-test",
"e2e-test": "protractor ./config/protractor.conf.js",
"e2e-update": "webdriver-manager update --standalone false --gecko false",
...
}
The updated tsconfig.spec.json:
"compilerOptions": {
...
"target": "es5",
"types": [
"jasmine",
"node"
],
"baseUrl": "../src",
"paths": {
"@app/*": [ "app/*" ],
"@assets/*": [ "assets/*" ],
"@env": [ "environments/environment" ],
"@pages/*": [ "pages/*" ],
"@services/*": [ "services/*" ],
"@tests/*": [ "./*" ],
"@theme/*": [ "theme/*" ]
}
},
...
Putting it all together
I started with a simple test to make sure that the setup works as expected:
import { ComponentFixture, async } from '@angular/core/testing';
import { TestUtils } from '@tests/test';
import { SignInPage } from '@pages/sign-in/sign-in';
let fixture: ComponentFixture<SignInPage> = null;
let instance: any = null;
describe('Page: Sign in', () => {
beforeEach(async(() => TestUtils.beforeEachCompiler([SignInPage]).then(compiled => {
fixture = compiled.fixture;
instance = compiled.instance;
fixture.detectChanges();
})));
afterEach(() => {
fixture.destroy();
});
it('Should create the Sign in page', async(() => {
expect(instance).toBeTruthy();
}));
});
Let's try running it:
npm test
After a few moments, karma opens a browser and starts writing to the console:
Your console output should look something like:
INFO: 'SignInPage initialised'
Chrome 62.0.3202 (Mac OS X 10.12.6): Executed 0 of 1 SUCCESS (0 secs / 0 secs)
Chrome 62.0.3202 (Mac OS X 10.12.6): Executed 1 of 1 SUCCESS (0 secs / 0.335 secChrome 62.0.3202 (Mac OS X 10.12.6): Executed 1 of 1 SUCCESS (0.344 secs / 0.335 secs)
Source Code:
- GitHub: The Big Top App
Unit and End-to-End Testing Resources:
- Stephen Hazleton: Unit Testing an Ionic 2 project
- Stephen Hazleton: Deprecate in favour of 'ionic-team/ionic-unit-testing-example'
- Angular docs: Introduction to Angular testing
- Ionic blog: Basic Unit Testing in Ionic
- GitHub ionic-team: Example of adding unit testing in your Ionic 2 App
Mobile Security Testing Resources:
- GitHub: OWASP Mobile Security Testing Guide
- GitHub: Mobile Security Testing Tools
- OWASP [dot] org: Mobile AppSec Verification
- Apache Cordova docs: Security Guide