Ionic Testing Task
This is an in-class activity to prepare you for automating the testing of an Ionic application. Testing web applications uses many of the same tools. [Jump to Day 1 steps] [Jump to Day 2 steps]
Two kinds of testing are involved: unit and end to end tests.
There are several good sites that go into detail about testing Ionic and Ionic 2 apps, e.g., this site. Unfortunately, they require many installation steps and editing of configuration and JavaScript files. I've encountered fatal typos, especially in specifying subdirectories, and out-of-date library names, e.g., angular-cli instead of @angular/cli. Several reporting tools have changed names.
For now, the simplest starting point is this Github starter project. There are many more things you could add, as described on other Ionic and Angular testing sites. This will give you a working test set up. Then you can add the rest.
The Ionic developers have said that eventually the testing framework will be included in Ionic and this repository will be unnecessary. We'll see.
Install and Run Ionic 2 Test Demos
First, make sure testing works for you.
cd to your projects directory, then do the following:
git clone https://github.com/driftyco/ionic-unit-testing-example.git cd ionic-unit-testing-example npm install
There should be no errors at any step. There may be some warnings.
If, and only if, Github is not responding, download and extract files from this possibly out of date archive.
Run the unit tests
If everything installed, now run the unit tests with the following command:
npm test
The Chrome browser should open a new window showing the Karma home page. Click on the Debug button to see a report on the test results. It should say 4 tests were run, with 0 failures.
The default test script keeps running. Stop it with a command-C or control-C. Normally you would leave it running until there no failing tests. The script watches for code file changes and reruns the tests within a few seconds of any saved change. Reload the Debug page to see the new results, when tests finish.
Run the end-to-end tests
To run the end-to-end tests, you have to start the app first:
ionic serve
Assuming it starts without error, open a new command shell, cd to the project root, and run
npm run e2e
The results of the end-to-end tests are given in the console. There's just one end-to-end test in the demo. See end-to-end tests below.
Common Errors and Fixes
Update!
If any errors occur, the first thing to do it make sure you have an up-to-date NodeJS and up-to-date packages. Run
ionic info
If it shows a version of Node older than version 6, update Node.
When or not you updated Node, you should also update your package dependencies.
Unit test errors
These are errors that occur when running npm test:
- AppScript.serve is not a function: your @ionic/app-scripts package is out of date. The updating about should have fixed that, but if not, try npm install --save-dev @ionic/app-scripts@latest.
End-to-end test errors
These are errors that occur when running npm run e2e:
- Could not find Angular: you forgot to run ionic serve first.
- info(...msgs) .. SyntaxError: Unexpected token: your version of Node is too old to run Protractor
Adding Testing to An Existing Ionic App
Once the above works, add the testing tools to an existing app.
Install and Test the Sample Reddit Reader App
Reddit Reader is a hybrid app that displays posts on Reddit. It's not complete, but it works, looks nice, has a decent amount of complexity, and has absolutely no testing in it.
git clone https://github.com/DreamWebTeam/ionic2-reddit-reader.git cd ionic2-reddit-reader npm install
If, and only if, Github is not responding, download and extract files from this possibly out of date archive.
If everything installs, then test that it runs.
ionic serve
A page should appear showing some current reddit items.
Stop the server.
Add testing to the Reddit Reader
Use ionic-unit-testing-example as the basis for what needs to be added to the Reddit Reader.
Copy the directories e2e and test-config from ionic-unit-testing-example to ionic2-reddit-reader.
Update ionic2-reddit-reader/tsconfig.json to exclude test files from the normal build, i.e., the exclude: field should look like this:
... "exclude": [ "node_modules", "src/**/*.spec.ts" ], ...
Update the ionic2-reddit-reader/package.json to include the additional dependencies and scripts needed for testing. You can do this manually, or use a tool, like this:
- Go to mergely.
- Put the contents of ionic-unit-testing-example/package.json on the left.
- Put the contents of ionic2-reddit-reader/package.json on the right.
- Click Compare to get the differences between the two JSON objects.
- Carefully use the Merge change arrows
to copy every item in ionic-unit-testing-example/package.json
that is missing or newer than what's in
ionic2-reddit-reader/package.json.
- Don't change the app name, the Cordova dependencies, etc.
- When done, copy the contents of the updated JSON back into the file ionic2-reddit-reader/package.json.
Now update the project modules and trying running again.
npm install ionic serve
Add end-to-end tests
While the app is running, try the existing end-to-end test that you copied over from ionic-unit-testing-example.
Open a new commmand window. cd to the ionic2-reddit-reader project. Run the end-to-end tests.
npm run e2e
You should get a test failure, because the test looks for the page title "Page One" and this doesn't exist.
Now fix that test to look for a title that contains "Reddit Reader" instead. See the Jasmine documentation for examples of standard matchers.
Try running the test again. Repeat until it passes.
Congratulations! You just developed your first end-to-end test!
Add unit tests
Verify unit testing is working
To make sure that any unit tests are being found and executed, create a toy unit test. Use the example test in the Angular Testing tutorial.
- Add the toy src/app/1st.spec.ts unit test to your Reddit Reader.
- Verify that the test passes when you do npm test. Remember to click on the Debug button on the browser page to see the test report.
- Edit the test file to make it a failing test.
- The tests should re-run automatically, unless you force-quit the test script
- Delete the toy test.
Add app component test
For the next step, go to Ionic 2 Unit Testing: The Best Way.
Copy the file src/app/app.component.spec.ts from the article. Most of the boilerplate can stay. Two things need to change.
- The name of the app class is not MyApp.
- There is no pages member in the app class, so expect(component.pages.length).toBe(2) will not compile.
Open src/app/app.component.ts. There'll you find the name of the app to use in the test. You'll also see that the app class has a rootPage member. Change the test for 2 pages to be a test that the root page is the Posts page.
Save the changes. The tests should re-run automatically, if you have not force-quit the test script.
Congratulations! You just developed your first app component test!
Testing Tools
Three standard tools are used in this demo project:
- Karma test runner
- Jasmine
- Protractor for end-to-end testing of web apps
These are general tools. In addition, there is a fourth tool, specifically for Angular:
- TestBed for testing individual components, using mocks to stand in for the rest of the app.
Configuration Files
A number of configuration files have to be correctly defined for everything to work. Should something break, or should you need to add more tools, this is where to look.
- package.json -- always start here, where everything needed by your app is specified. Note that most of the testing tools are devDependencies because they are not needed to run your app.
- test-config/ -- this directory contains
all the config files for the testing tools. I like this arrangement
better than some examples that put the config files in the root.
- karma.conf.js configures the Karma test runner, mostly to say what other primary tools to use.
-
karma-test-shim.js loads a number of modules, and,
most importantly, says where to find test code. The line
var appContext = require.context('../src', true, /\.spec\.ts/
says that any file ending with .spec.ts in the src directory is a unit test file. -
protractor.conf.js configures the Protractor tool for end-to-end
tests. The line
specs: [ '../e2e/**/*.e2e-spec.ts' ]
says that any file ending with .e2e-spec.ts in the e2e directory is an end-to-end test file. - mocks-ionic.ts defines a mock Ionic platform to allow unit testing the app without serving Ionic.
Test Files
Most of the time, what you need to do is create test files. There are two kinds: unit tests, for testing components, and end-to-end tests, for testing the app, i.e., integration testing.
Unit Tests
Unit tests by definition test some unit of code, e.g., a function or a class, not an entire system. Unit tests should run very fast and not depend on external modules, the network, a database, etc.
The demo project includes two examples of unit tests. Both are testing Angular components. That means they are testing objects that normally are created and interacting inside an Angular app. To test these as independent units, the rest of the app has to be "mocked". The library for doing that is TestBed.
The recommended practice in Angular is to create a separate unit test file for every component you want to test, rather than one big test file. The file is placed next to the component, for convenience. The file name is the same as the component, but ending in .spec.ts.
Because this is such a simple app, there are only two unit tests. One is for the component representing the app itself, app.component.ts. Next to it is the test file, app.component.spec.ts
The other unit test is for one of the two HTML pages in the app. While it's easy to write code to look at HTML and test for certain properties, this quickly leads to fragile hard to read tests that break with minor changes in the user interface.
To avoid that problem, best practice is to create page objects. These are JavaScript objects that provide controlled access to the parts of a web page. The object takes care of the specific details of the page. If the page changes, the object may need to change. But all the testing is done using the page object's public functions, and this rarely changes.
The demo project has an example of a page object and the test for that object:
- page1.html is the actual Angular HTML.
- page1.ts is the page object. Objects are usually more complex but this just connects the object to the appropriate URL.
- page1.spec.ts is the test for the page object. Most of this is boilerplate. Just the last few lines are the real test, which use the regular expression /ionic/i to see if the page mentions Ionic.
End-to-end Tests
End-to-end tests test the actual app, using Protractor and Selenium to communicate with a running web version of the app.
Because the tests are not component-specific, the tests are stored together in the directory e2e, and no mocking is done.
The example project has one end-to-end test, app.e2e-spec.ts, that just checks to see if opening the app displays a page that appears to have the right content. As with the page component unit test, it uses a page object, app.po.ts to insulate the test from small changes in the web page.
A test like this serves as a good "is it up?" test that you run to make sure you haven't completely broken the app with some change.