block includes
include ../_util-fns
- var _JavaScript = 'JavaScript';
//- Double underscore means don't escape var, use !{__var}.
- var __chaining_op = ';
or ,
';
- var __new_op = 'new
';
- var __objectAsMap = 'object';
:marked
This chapter offers tips and techniques for testing Angular applications.
Along the way you will learn some general testing principles and techniques but the focus is on
Angular testing.
#top
:marked
# Table of Contents
1. [Introduction to Angular Testing](#testing-intro)
1. [Setup](#setup)
- [setup files](#setup-files): `karma.conf`, `karma-test-shim`, `systemjs.config`
- [npm packages](#npm-packages)
1. [The first karma test](#1st-karma-test)
1. [The Angular Testing Platform (ATP) ](#atp-intro)
1. [The sample application and its tests](#sample-app)
1. [A simple component test](#simple-component-test)
- [_configureTestingModule_](#configure-testing-module)
- [_createComponent_](#create-component)
- [_detectChanges_](#detect-changes)
- [_autoDetectChanges_](#auto-detect-changes)
1. [Test a component with a service dependency](#component-with-dependency)
1. [Test a component with an async service](#component-with-async-service)
- [spies](#service-spy)
- [_async_](#async)
- [_whenStable_](#when-stable)
- [_fakeAsync_](#async)
- [_tick_](#tick)
- [_jasmine.done_](#jasmine-done)
1. [Test a component with an external template](#component-with-external-template)
- [_async_](#async-in-before-each) in `beforeEach`
- [_compileComponents_](#compile-components)
1. [Test a component with inputs and outputs](#component-with-inputs-output)
1. [Test a component inside a test host component](#component-inside-test-host)
1. [Test a routed component](#routed-component)
- [_inject_](#inject)
1. [Test a routed component with parameters](#routed-component-w-param)
- [_Observable_ test double](#stub-observable)
1. [Use a _page_ object to simplify setup](#page-object)
1. [Setup with module imports](#import-module)
1. [Override component providers](#component-override)
1. [Test a _RouterOutlet_ component](#router-outlet-component)
- [stubbing unneeded components](#stub-component)
- [Stubbing the _RouterLink_](#router-link-stub)
- [_By.directive_ and injected directives](#by-directive)
1. ["Shallow" component tests with *NO\_ERRORS\_SCHEMA*](#shallow-component-test)
1. [Test an attribute directive](#attribute-directive)
1. [Isolated tests](#testing-without-atp "Testing without the Angular Testing Platform")
- [Services](#isolated-service-tests)
- [Pipes](#isolated-pipe-tests)
- [Components](#isolated-component-tests)
1. [_Angular Testing Platform_ API](#atp-api)
- [Stand-alone functions](#atp-api): `async`, `fakeAsync`, etc.
- [_TestBed_](#testbed-class-summary)
- [_ComponentFixture_](#component-fixture-class-summary)
- [_DebugElement_](#debug-element-details)
1. [FAQ](#faq "Frequently asked questions")
:marked
It’s a big agenda. Fortunately, you can learn a little bit at a time and put each lesson to use.
# Live examples
The chapter sample code is available as live examples for inspection, experiment, and download.
* karma.conf.js
td
:marked
The karma configuration file that specifies which plug-ins to use,
which application and test files to load, which browser(s) to use,
and how to report test results.
It loads three other setup files:
* `systemjs.config.js`
* `systemjs.config.extras.js`
* `karma-test-shim.js`
tr
td(style="vertical-align: top") karma-test-shim.js
td
:marked
This shim prepares karma specifically for the Angular test environment
and launches karma itself.
It loads the `systemjs.config.js` file as part of that process.
tr
td(style="vertical-align: top") systemjs.config.js
td
:marked
[SystemJS](https://github.com/systemjs/systemjs/blob/master/README.md)
loads the application and test files.
This script tells SystemJS where to find those files and how to load them.
It's the same version of `systemjs.config.js` used by QuickStart-based applications.
tr
td(style="vertical-align: top") systemjs.config.extras.js
td
:marked
An optional file that supplements the SystemJS configuration in `systemjs.config.js` with
configuration for the specific needs of the application itself.
A stock `systemjs.config.js` can't anticipate those needs.
You fill the gaps here.
The sample version for this chapter adds the **model barrel**
to the SystemJs `packages` configuration.
tr
td(colspan="2")
+makeExample('testing/ts/systemjs.config.extras.js', '', 'systemjs.config.extras.js')(format='.')
:marked
### npm packages
The sample tests are written to run in Jasmine and karma.
The two "fast path" setups added the appropriate Jasmine and karma npm packages to the
`devDependencies` section of the `package.json`.
They were installed when you ran `npm install`.
.l-hr
#1st-karma-test
:marked
# The first karma test
Start with a simple test to make sure the setup works properly.
Create a new file called `1st.spec.ts` in the application root folder, `app/`
.alert.is-important
:marked
Tests written in Jasmine are called _specs_ .
**The filename extension must be `.spec.ts`**,
the convention adhered to by `karma.conf.js` and other tooling.
:marked
**Put spec files somewhere within the `app/` folder.**
The `karma.conf.js` tells karma to look for spec files there,
for reasons explained [below](#spec-file-location).
Add the following code to `app/1st.spec.ts`.
+makeExample('testing/ts/app/1st.spec.ts', '', 'app/1st.spec.ts')(format='.')
:marked
## Run karma
Compile and run it in karma from the command line.
.l-sub-section
:marked
The QuickStart repo adds the following command to the `scripts` section in `package.json`.
code-example(format="." language="bash").
"test": "tsc && concurrently \"tsc -w\" \"karma start karma.conf.js\"",
:marked
Add that to your `package.json` if it's not there already.
:marked
Open a terminal or command window and enter
code-example(format="." language="bash").
npm test
:marked
The command compiles the application and test code a first time.
If the compile fails, the command aborts.
If it succeeds, the command re-compiles (this time in watch mode) in one process
and starts karma in another.
Both processes watch pertinent files and re-run when they detect changes.
After a few moments, karma opens a browser ...
figure.image-display
img(src='/resources/images/devguide/testing/karma-browser.png' style="width:400px;" alt="Karma browser")
:marked
... and starts writing to the console.
Hide (don't close!) the browser and focus on the console output which should look something like this.
code-example(format="." language="bash").
> npm test
> tsc && concurrently "tsc -w" "karma start karma.conf.js"
[0] 1:37:03 PM - Compilation complete. Watching for file changes.
[1] 24 07 2016 13:37:09.310:WARN [karma]: No captured browser, open http://localhost:9876/
[1] 24 07 2016 13:37:09.361:INFO [karma]: Karma v0.13.22 server started at http://localhost:9876/
[1] 24 07 2016 13:37:09.370:INFO [launcher]: Starting browser Chrome
[1] 24 07 2016 13:37:10.974:INFO [Chrome 51.0.2704]: Connected on socket /#Cf6A5PkvMzjbbtn1AAAA with id 24600087
[1] Chrome 51.0.2704: Executed 0 of 0 SUCCESS
Chrome 51.0.2704: Executed 1 of 1 SUCCESS
SUCCESS (0.005 secs / 0.005 secs)
:marked
Both the compiler and karma continue to run. The compiler output is preceeded by `[0]`;
the karma output by `[1]`.
Change the expectation from `true` to `false`.
The _compiler_ watcher detects the change and recompiles.
code-example(format="." language="bash").
[0] 1:49:21 PM - File change detected. Starting incremental compilation...
[0] 1:49:25 PM - Compilation complete. Watching for file changes.
:marked
The _karma_ watcher detects the change to the compilation output and re-runs the test.
code-example(format="." language="bash").
[1] Chrome 51.0.2704: Executed 0 of 1 SUCCESS
Chrome 51.0.2704 1st tests true is true FAILED
[1] Expected false to equal true.
[1] Chrome 51.0.2704: Executed 1 of 1 (1 FAILED) (0.005 secs / 0.005 secs)
:marked
It failed of course.
Restore the expectation from `false` back to `true`.
Both processes detect the change, re-run, and karma reports complete success.
.alert.is-helpful
:marked
The console log can be quite long. Keep your eye on the last line.
It says `SUCCESS` when all is well.
If it says `FAILED`, scroll up to look for the error or, if that's too painful,
pipe the console output to a file and inspect with your favorite editor.
code-example(format="." language="json").
npm test > spec-output.txt
:marked
## Test debugging
Debug specs in the browser in the same way you debug an application.
- Reveal the karma browser window (hidden earlier).
- Open the browser's “Developer Tools” (F12 or Ctrl-Shift-I).
- Pick the “sources” section
- Open the `1st.spec.ts` test file (Ctrl-P, then start typing the name of the file).
- Set a breakpoint in the test
- Refresh the browser … and it stops at the breakpoint.
figure.image-display
img(src='/resources/images/devguide/testing/karma-1st-spec-debug.png' style="width:700px;" alt="Karma debugging")
a(href="#top").to-top Back to top
.l-hr
#atp-intro
:marked
# The Angular Testing Platform (ATP)
Many tests explore how applications classes interact with Angular and the DOM while under Angular's control.
Such tests are easy to write with the help of the _Angular Testing Platform_ (ATP)
which consists of the `TestBed` class and some helper functions.
Tests written with the _Angular Testing Platform_ are the main focus of this chapter.
But they are not the only tests you should write.
### Isolated unit tests
You can and should write [isolated unit tests](#testing-without-atp "Testing without the Angular Testing Platform")
for components, directives, pipes, and services.
Isolated unit tests examine an instance of a class all by itself without
any dependence on Angular or any injected values.
The tester creates a test instance of the class with new, supplying test doubles for the constructor parameters as needed, and
then probes the test instance API surface.
Isolated tests don't reveal how the class interacts with Angular.
In particular, they can't reveal how a component class interacts with its own template or with other components.
Those tests require the Angular Testing Platform.
### Testing with the _ Angular Testing Platform_
The _Angular Testing Platform_ consists of the `TestBed` class and some helper functions from `@angular/core/testing`.
The `TestBed` creates an Angular testing module — an `@NgModule` class —
that you configure to produce the module environment for the class you want to test.
You tell the `TestBed` to create an instance of the test component and probe that instance with tests.
That's the `TestBed` in a nutshell.
In practice, you work with the static methods of the `TestBed` class.
These static methods create and update a fresh hidden `TestBed` instance before each Jasmine `it`.
.l-sub-section
:marked
You can access that hidden instance anytime by calling `getTestBed()`;
:marked
Thanks to initialization in the [testing shims](#setup),
the default `TestBed` instance is pre-configured with a baseline of default providers and declarables (components, directives, and pipes)
that almost everyone needs.
The shims in this chapter are designed for testing a browser application so the default configuration includes the `CommonModule` declarables from `@angular/common`
and the `BrowserModule` providers (some of them mocked) from `@angular/platform-browser`.
This default testing module configuration is a _foundation_ for testing _any_ browser app.
You call `TestBed.configureTestingModule` with an object that defines additional imports, declarations, providers and schemas
to reshape the testing module to fit your application tests.
Optional `override...` methods can fine-tune aspects of the configuration.
After configuring the `TestBed`, tell it to create an instance of the test component and the test fixture
that you'll need to inspect and control the component's immediate environment.
+makeExample('testing/ts/app/banner.component.spec.ts', 'simple-example-before-each', 'app/banner.component.spec.ts (simplified)')(format='.')
:marked
Angular tests can interact with the HTML in the test DOM,
simulate user activity, tell Angular to perform specific task (such as change detection),
and see the effects of these actions both in the test component and in the test DOM.
+makeExample('testing/ts/app/banner.component.spec.ts', 'simple-example-it', 'app/banner.component.spec.ts (simplified)')(format='.')
:marked
A comprehensive review of the _Angular Testing Platform_ APIs appears [later in the chapter](#atp-api).
Let's dive right into Angular testing, starting with with the components of a sample application.
a(href="#top").to-top Back to top
.l-hr
#sample-app
:marked
# The sample application and its tests
This chapter tests a cut-down version of the _Tour of Heroes_ [tutorial app](../tutorial).
The following live example shows how it works and provides the complete source code.
Give it some time to load and warm up.
:marked
The following live example runs all the tests of this application
inside the browser, using the Jasmine Test Runner instead of karma.
It includes the tests discussed in this chapter and additional tests for you to explore.
This live example contains both application and test code.
It is large and can take up to a minute to start. Please be patient.