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. * The sample application * The first spec * The complete application specs * A grab bag of demonstration specs a(href="#top").to-top Back to top .l-hr #testing-intro :marked # Introduction to Angular Testing You write tests to explore and confirm the behavior of the application. 1. They **guard** against changes that break existing code (“regressions”). 1. They **clarify** what the code does both when used as intended and when faced with deviant conditions. 1. They **reveal** mistakes in design and implementation. Tests shine a harsh light on the code from many angles. When a part of the application seems hard to test, the root cause is often a design flaw, something to cure now rather than later when it becomes expensive to fix. This chapter assumes that you know something about testing. Don't worry if you don't. There are plenty of books and online resources to get up to speed. ## Tools and Technologies You can write and run Angular tests with a variety of tools and technologies. This chapter describes specific choices that are known to work well. table(width="100%") col(width="20%") col(width="80%") tr th Technology th Purpose tr(style=top) td(style="vertical-align: top") Jasmine td :marked The [Jasmine test framework](http://jasmine.github.io/2.4/introduction.html). provides everything needed to write basic tests. It ships with an HTML test runner that executes tests in the browser. tr(style=top) td(style="vertical-align: top") Angular Testing Platform td :marked The Angular Testing Platform creates a test environment and harness for the application code under test. Use it to condition and control parts of the application as they interact _within_ the Angular environment. tr(style=top) td(style="vertical-align: top") Karma td :marked The [karma test runner](https://karma-runner.github.io/1.0/index.html) is ideal for writing and running tests while developing the application. It can be an integral part of the application build process. This chapter describes how to setup and run tests with karma. tr(style=top) td(style="vertical-align: top") Protractor td :marked Use protractor to write and run _end-to-end_ (e2e) tests. End-to-end tests explore the application _as users experience it_. In e2e testing, one process runs the real application and a second process runs protractor tests that simulate user behavior and assert that the application responds in the browser as expected. .l-hr #setup :marked # Setup Many think writing tests is fun. Few enjoy setting up the test environment. To get to the fun as quickly as possible, the deep details of setup appear later in the chapter (_forthcoming_). A bare minimum of discussion plus the downloadable source code must suffice for now. There are two fast paths to getting started. 1. Start a new project following the instructions in the [QuickStart github repository](https://github.com/angular/quickstart/blob/master/README.md). 1. Start a new project with the [Angular CLI](https://github.com/angular/angular-cli/blob/master/README.md). Both approaches install **npm packages, files, and scripts** pre-configured for applications built in their respective modalities. Their artifacts and procedures differ slightly but their essentials are the same and there are no differences in the test code. In this chapter, the application and its tests are based on the QuickStart repo. .alert.is-helpful :marked If your application was based on the QuickStart repository, you can skip the rest of this section and get on with your first test. The QuickStart repo provides all necessary setup. #setup-files :marked ### Setup files Here's brief description of the setup files. table(width="100%") col(width="20%") col(width="80%") tr th File th Description tr td(style="vertical-align: top") 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. a(href="#top").to-top Back to top .l-hr #simple-component-test :marked # Test a component The top of the screen displays application title, presented by the `BannerComponent` in `app/banner.component.ts`. +makeExample('testing/ts/app/banner.component.ts', '', 'app/banner.component.ts')(format='.') :marked `BannerComponent` has an inline template and an interpolation binding, about as simple as it gets. Probably too simple to be worth testing in real life but perfect for a first encounter with the `TestBed`. The corresponding `app/banner-component.spec.ts` sits in the same folder as the component, for reasons explained [here](#q-spec-file-location); Start with ES6 import statements to get access to symbols referenced in the spec. +makeExample('testing/ts/app/banner.component.spec.ts', 'imports', 'app/banner.component.spec.ts (imports)')(format='.') #configure-testing-module :marked Here's the setup for the tests followed by observations about the `beforeEach`: +makeExample('testing/ts/app/banner.component.spec.ts', 'setup', 'app/banner.component.spec.ts (setup)')(format='.') :marked `TestBed.configureTestingModule` takes an `@NgModule`-like metadata object. This one simply declares the component to test, `BannerComponent`. It lacks `imports` because (a) it extends the default testing module configuration which already has what `BannerComponent` needs and (b) `BannerComponent` doesn't interact with any other components. The configuration could have imported `AppModule` (which declares `BannerComponent`). But that would lead to tons more configuration in order to support the other components within `AppModule` that have nothing to do with `BannerComponent`. #create-component :marked `TestBed.createComponent` creates an instance of `BannerComponent` to test. The method returns a `ComponentFixture`, a handle on the test environment surrounding the created component. The fixture provides access to the component instance itself and to the `DebugElement` which is a handle on the component's DOM element. Query the `DebugElement` by CSS selector for the `

` sub-element that holds the actual title. ### _createComponent_ closes configuration `TestBed.createComponent` closes the current `TestBed` instance to further configuration. You cannot call any more `TestBed` configuration methods, not `configureTestModule` nor any of the `override...` methods. The `TestBed` throws an error if you try. .alert.is-important :marked Do not configure the `TestBed` after calling `createComponent`. :marked ### The tests Jasmine runs this `beforeEach` before each test of which there are two +makeExample('testing/ts/app/banner.component.spec.ts', 'tests', 'app/banner.component.spec.ts (tests)')(format='.') :markdown These tests ask the `DebugElement` for the native HTML element to satisfy their expectations. #detect-changes :marked ### _detectChanges_: Angular change detection under test Each test tells Angular when to perform change detection by calling `fixture.detectChanges()`. The first test does so immediately, triggering data binding and propagation of the `title` property to the DOM element. The second test changes the component's `title` property _and only then_ calls `fixture.detectChanges()`; the new value appears in the DOM element. In production, change detection kicks in automatically when Angular creates a component or the user enters a keystroke or an asynchronous activity (e.g., AJAX) completes. The `TestBed.createComponent` does _not_ trigger change detection. The fixture does not automatically push the component's `title` property value into the data bound element, a fact demonstrated in the following test: +makeExample('testing/ts/app/banner.component.spec.ts', 'test-w-o-detect-changes', 'app/banner.component.spec.ts (no detectChanges)')(format='.') :marked This behavior (or lack of it) is intentional. It gives the tester an opportunity to investigate the state of the component _before Angular initiates data binding or calls lifecycle hooks_. #auto-detect-changes :marked ### Automatic change detection Some testers prefer that the Angular test environment run change detection automatically. That's possible by configuring the `TestBed` with the _AutoDetect_ provider: +makeExample('testing/ts/app/banner.component.spec.ts', 'auto-detect', 'app/banner.component.spec.ts (AutoDetect)')(format='.') :marked Here are three tests that illustrate how _auto-detect_ works. +makeExample('testing/ts/app/banner.component.spec.ts', 'auto-detect-tests', 'app/banner.component.spec.ts (AutoDetect Tests)')(format='.') :marked The first test shows the benefit of automatic change detection. The second and third test remind us that Angular does _not_ know about changes to component property values unless Angular itself (or some asynchronous process) makes the change. This is as true in production as it is in test. In production, external forces rarely change component properties like this, whereas these kinds of probing changes are typical in unit tests. The tester will have to call `fixture.detectChanges()` quite often despite having opted into auto detect. .alert.is-helpful :marked Rather than wonder when the test fixture will or won't perform change detection, the samples in this chapter _always call_ `detectChanges()` _explicitly_. a(href="#top").to-top Back to top .l-hr #component-with-dependency :marked # Test a component with a dependency Components often have service dependencies. The `WelcomeComponent` displays a welcome message to the logged in user. It knows who the user is based on a property of the injected `UserService`: +makeExample('testing/ts/app/welcome.component.ts', '', 'app/welcome.component.ts')(format='.') :marked The `WelcomeComponent` has decision logic that interacts with the service; such logic makes this component worth testing. Here's the testing module configuration for the spec file, `app/welcome.component.spec.ts`: +makeExample('testing/ts/app/welcome.component.spec.ts', 'config-test-module', 'app/welcome.component.spec.ts')(format='.') :marked This time, in addition to declaring the component under test, the configurations sets the `providers` list with the dependent `UserService`. This example configures the testing module with a stub `UserService`. ## Provide service test doubles A component under test doesn't have to be injected with real services. In fact, it is usually better if they are test doubles (stubs, fakes, spies, or mocks). The purpose of the spec is to test the component, not the service, and real services can be trouble. Injecting the real `UserService` could be a nightmare. The real service might try to ask the user for login credentials and try to reach an authentication server. These behaviors could be hard to intercept. It is far easier to create and register a test double in place of the real `UserService`. This particular test suite supplies a minimal `UserService` stub that satisfies the needs of the `WelcomeComponent` and its tests: +makeExample('testing/ts/app/welcome.component.spec.ts', 'user-service-stub')(format='.') #injected-service-reference :marked ## Referencing injected services The tests need access to the injected (stubbed) `UserService`. You cannot reference the `userServiceStub` object provided to the testing module. **It does not work!** Surprisingly, the instance actually injected into the component is _not the same_ object as the provided `userServiceStub`. .alert.is-important :marked Always use an injector to get a reference to an injected service. :marked Where do you get the injector? Angular has an hierarchical injection system. In a test there can be injectors at multiple levels. The current `TestBed` injector creates a top-level injector. The `WelcomeComponent` injector is a child of that injector created specifically for the component. You can get a `UserService` from the current `TestBed` injector by calling `TestBed.get`. +makeExample('testing/ts/app/welcome.component.spec.ts', 'inject-from-testbed', 'TestBed injector')(format='.') .l-sub-section :marked The [inject](#inject) function is another way to inject one or more services into a test. :marked That happens to work for testing the `WelcomeComponent` because the `UserService` instance from the `TestBed` is the same as the `UserService` instance injected into the component. That won't always be the case. Be absolutely sure to reference the service instance that the component is _actually receiving_, Call `get` on the component's injector which is `fixture.debugElement.injector`: +makeExample('testing/ts/app/welcome.component.spec.ts', 'injected-service', 'Component\'s injector')(format='.') .alert.is-important :marked Use the component's own injector to get the component's injected service. #welcome-spec-setup :marked Here's the complete, preferred `beforeEach`: +makeExample('testing/ts/app/welcome.component.spec.ts', 'setup', 'app/welcome.component.spec.ts')(format='.') :marked And here are some tests: +makeExample('testing/ts/app/welcome.component.spec.ts', 'tests', 'app/welcome.component.spec.ts')(format='.') :marked The first is a sanity test; it confirms that the stubbed `UserService` is called and working. The remaining tests confirm the logic of the component when the service returns different values. The second test validates the effect of changing the user name. The third test checks that the component displays the proper message when there is no logged-in user. a(href="#top").to-top Back to top .l-hr #component-with-async-service :marked # Test a component with an async service Many services return values asynchronously. Most data services make an HTTP request to a remote server and the response is necessarily asynchronous. The "About" view in this sample displays Mark Twain quotes. The `TwainComponent` handles the display, delegating the server request to the `TwainService`. Both are in the `app/shared` folder because the author intends to display Twain quotes on other pages someday. Here is the `TwainComponent`. +makeExample('testing/ts/app/shared/twain.component.ts', 'component', 'app/shared/twain.component.ts')(format='.') :marked The `TwainService` implementation is irrelevant at this point. It is sufficient to see within `ngOnInit` that `twainService.getQuote` returns a promise which means it is asynchronous. In general, tests should not make calls to remote servers. They should emulate such calls. The setup in this `app/shared/twain.component.spec.ts` shows one way to do that: +makeExample('testing/ts/app/shared/twain.component.spec.ts', 'setup', 'app/shared/twain.component.spec.ts (setup)')(format='.') #service-spy :marked ### Spying on the real service This setup is similar to the [`welcome.component.spec` setup](#welcome-spec-setup). But instead of creating a stubbed service object, it injects the _real_ service (see the testing module `providers`) and replaces the critical `getQuote` method with a Jasmine spy. +makeExample('testing/ts/app/shared/twain.component.spec.ts', 'spy')(format='.') :marked The spy is designed such that any call to `getQuote` receives an immediately resolved promise with a test quote. The spy bypasses the actual `getQuote` method and therefore will not contact the server. .l-sub-section :marked Faking a service instance and spying on the real service are _both_ great options. Pick the one that seems easiest for the current test suite. Don't be afraid to change your mind. :marked Here are the tests with commentary to follow: +makeExample('testing/ts/app/shared/twain.component.spec.ts', 'tests', 'app/shared/twain.component.spec.ts (tests)') :marked ### Synchronous tests The first two tests are synchronous. Neither test can prove that a value from the service will be displayed. Thanks to the spy, the second test verifies that `getQuote` is called. But the quote itself has not arrived, despite the fact that the spy returns a resolved promise. This test must wait at least one full turn of the JavaScript engine, a least one "tick", before the value becomes available. By that time, the test runner has moved on to the next test in the suite. The test must become an "async test" ... like the third test #async :marked ## The _async_ function in _it_ Notice the `async` in the third test. +makeExample('testing/ts/app/shared/twain.component.spec.ts', 'async-test', 'app/shared/twain.component.spec.ts (async test)')(format='.') :marked The `async` function is an independent feature of the _Angular Testing Platform_. It simplifyies coding of asynchronous tests by arranging for the tester's code to run in a special _async test zone_. The `async` function _takes_ a parameterless function and _returns_ a parameterless function which becomes the argument to the Jasmine `it` call. The body of the `async` argument looks much like the body of a normal `it` argument. There is nothing obviously asynchronous about it. For example, it doesn't return a promise and there is no `done` function to call as there is in standard Jasmine asynchronous tests. Some functions called within a test (such as `fixture.whenStable`) continue to reveal their asynchronous behavior. Consider also the [_fakeAsync_](#fake-async) alternative which affords a more linear coding experience. #when-stable :marked ## _whenStable_ The test must wait for the `getQuote` promise to resolve. The `getQuote` promise promise resolves in the next turn of the JavaScript engine, thanks to the spy. But a different test implementation of `getQuote` could take longer. An integration test might call the _real_ `getQuote`, resulting in an XHR request that took many seconds to respond. This test has no direct access to the promise returned by the call to `testService.getQuote` which is private and inaccessible inside `TwainComponent`. Fortunately, the `getQuote` promise is accessible to the _async test zone_ which intercepts all promises issued within the _async_ method call. The `ComponentFixture.whenStable` method returns its own promise which resolves when the `getQuote` promise completes. In fact, the _whenStable_ promise resolves when _all pending asynchronous activities_ complete ... the definition of "stable". Then the testing continues. The test kicks off another round of change detection (`fixture.detechChanges`) which tells Angular to update the DOM with the quote. The `getQuote` helper method extracts the display element text and the expectation confirms that the text matches the test quote. #fakeAsync #fake-async :marked ## The _fakeAsync_ function The fourth test verifies the same component behavior in a different way. +makeExample('testing/ts/app/shared/twain.component.spec.ts', 'fake-async-test', 'app/shared/twain.component.spec.ts (fakeAsync test)')(format='.') :marked Notice that `fakeAsync` replaces `async` as the `it` argument. The `fakeAsync` function is another, independent feature of the _Angular Testing Platform_. Like [async](#), it _takes_ a parameterless function and _returns_ a parameterless function which becomes the argument to the Jasmine `it` call. The `fakeAsync` function enables a linear coding style by running the test body in a special _fakeAsync test zone_. The principle advantage of `fakeAsync` over `async` is that the test appears to be synchronous. There are no promises at all. No `then(...)` chains to disrupt the visible flow of control. There are limitations. For example, you cannot make an XHR call from within a `fakeAsync`. #tick #tick-first-look :marked ## The _tick_ function Compare the third and fourth tests. Notice that `fixture.whenStable` is gone, replaced by `tick()`. The `tick` function is a part of the _Angular Testing Platform_ and a companion to `fakeAsync`. It can only be called within a `fakeAsync` body. Calling `tick()` simulates the passage of time until all pending asynchronous activities complete, including the resolution of the `getQuote` promise in this test case. It returns nothing. There is no promise to wait for. Proceed with the same test code as formerly appeared within the `whenStable.then()` callback. Even this simple example is easier to read than the third test. To more fully appreciate the improvement, imagine a succession of asynchronous operations, chained in a long sequence of promise callbacks. #jasmine-done :marked ## _jasmine.done_ While `fakeAsync` and even `async` function greatly simplify Angular asynchronous testing, you can still fallback to the traditional Jasmine asynchronous testing technique. You can still pass `it` a function that takes a [`done` callback](http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support). Now you are responsible for chaining promises, handling errors, and calling `done` at the appropriate moment. Here is a `done` version of the previous two tests: +makeExample('testing/ts/app/shared/twain.component.spec.ts', 'done-test', 'app/shared/twain.component.spec.ts (done test)')(format='.') :marked Although we have no direct access to the `getQuote` promise inside `TwainComponent`, the spy does and that makes it possible to wait for `getQuote` to finish. The `jasmine.done` technique, while discouraged, may become necessary when neither `async` nor `fakeAsync` can tolerate a particular asynchronous activity. That's rare but it happens. a(href="#top").to-top Back to top .l-hr #component-with-external-template :marked # Test a component with an external template The `TestBed.createComponent` is a synchronous method. It assumes that everything it could need is already in memory. That has been true so far. Each tested component's `@Component` metadata has a `template` property specifying an _inline templates_. Neither component had a `styleUrls` property. Everything necessary to compile them was in memory at test runtime. The `DashboardHeroComponent` is different. It has an external template and external css file, specified in `templateUrl` and `styleUrls` properties. +makeExample('testing/ts/app/dashboard/dashboard-hero.component.ts', 'component', 'app/dashboard/dashboard-hero.component.ts (component)')(format='.') :marked The compiler must read these files from a file system before it can create a component instance. The `TestBed.compileComponents` method asynchronously compiles all the components configured in its current testing module. After it completes, external templates and css files, have been "inlined" and `TestBed.createComponent` can do its job synchronously. .l-sub-section :marked WebPack developers need not call `compileComponents` because it inlines templates and css as part of the automated build process that precedes running the test. :marked The `app/dashboard/dashboard-hero.component.spec.ts` demonstrates the pre-compilation process: +makeExample('testing/ts/app/dashboard/dashboard-hero.component.spec.ts', 'compile-components', 'app/dashboard/dashboard-hero.component.spec.ts (compileComponents)')(format='.') #async-in-before-each :marked ## The _async_ function in _beforeEach_ Notice the `async` call in the `beforeEach`. The `async` function arranges for the tester's code to run in a special _async test zone_ that hides the mechanics of asynchronous execution, just as it does when passed to an [_it_ test)(#async). #compile-components :marked ## _compileComponents_ In this example, `Testbed.compileComponents` compiles one component, the `DashboardComponent`. It's the only declared component in this testing module. Tests later in this chapter have more declared components and some of them import application modules that declare yet more components. Some or all of these components could have external templates and css files. `TestBed.compileComponents` compiles them all asynchonously at one time. The `compileComponents` method returns a promise so you can perform additional tasks _after_ it finishes. ### _compileComponents_ closes configuration After `compileComponents` runs, the current `TestBed` instance is closed to further configuration. You cannot call any more `TestBed` configuration methods, not `configureTestModule` nor any of the `override...` methods. The `TestBed` throws an error if you try. .alert.is-important :marked Do not configure the `TestBed` after calling `compileComponents`. Make `compileComponents` the last step before calling `TestBed.createInstance` to instantiate the test component. :marked The `DashboardHeroComponent` spec follows the asynchonous `beforeEach` with a _synchronous_ `beforeEach` that completes the setup steps and runs tests ... as described in the next section. .l-hr #component-with-inputs-outputs :marked # Test a component with inputs and outputs A component with inputs and outputs typically appears inside the view template of a host component. The host uses a property binding to set the input property and uses an event binding to listen to events raised by the output property. The testing goal is to verify that such bindings work as expected. The tests should set input values and listen for output events. The `DashboardHeroComponent` is tiny example of a component in this role. It displays an individual heroe provided by the `DashboardComponent`. Clicking that hero tells the the `DashboardComponent` that the user has selected the hero. The `DashboardHeroComponent` is embedded in the `DashboardComponent` template like this: +makeExample('testing/ts/app/dashboard/dashboard.component.html', 'dashboard-hero', 'app/dashboard/dashboard.component.html (excerpt)')(format='.') :marked The `DashboardHeroComponent` appears in an `*ngFor` repeater which sets each component's `hero` input property to the iteration value and listens for the components `selected` event. Here's the component's definition again: +makeExample('testing/ts/app/dashboard/dashboard-hero.component.ts', 'component', 'app/dashboard/dashboard-hero.component.ts (component)')(format='.') :marked While testing a component this simple has little intrinsic value, it's worth knowing how. Three approaches come to mind: 1. Test it as used by `DashboardComponent` 1. Test it as a stand-alone component 1. Test it as used by a substitute for `DashboardComponent` A quick look at the `DashboardComponent` constructor discourages the first approach: +makeExample('testing/ts/app/dashboard/dashboard.component.ts', 'ctor', 'app/dashboard/dashboard.component.ts (constructor)')(format='.') :marked The `DashboardComponent` depends upon the Angular router and the `HeroService`. You'd probably have to replace them both with test doubles and that looks like a lot of work. The router seems particularly challenging. .l-sub-section :marked The [discussion below](#routed-component) covers testing components that requre the router. :marked The immediate goal is to test the `DashboardHeroComponent`, not the `DashboardComponent`, and there's no need to work hard unnecessarily. Let's try the second and third options. ## Test _DashboardHeroComponent_ stand-alone Here's the spec file setup. +makeExample('testing/ts/app/dashboard/dashboard-hero.component.spec.ts', 'setup', 'app/dashboard/dashboard-hero.component.spec.ts (setup)')(format='.') :marked The async `beforeEach` was discussed [above](#component-with-external-template). Having compiled the components asynchronously with `compileComponents`, the rest of the setup proceeds _synchronously_ in a _second_ `beforeEach`, using the basic techniques described [earlier](#simple-component-test). Note how the setup code assigns a test hero (`expectedHero`) to the component's `hero` property, emulating the way the `DashboardComponent` would set it via the property binding in its repeater. The first test follows: +makeExample('testing/ts/app/dashboard/dashboard-hero.component.spec.ts', 'name-test', 'app/dashboard/dashboard-hero.component.spec.ts (name test)')(format='.') :marked It verifies that the hero name is propagated through to template with a binding. There's a twist. The template passes the hero name through the Angular `UpperCasePipe` so the test must match the element value with the uppercased name: +makeExample('testing/ts/app/dashboard/dashboard-hero.component.html')(format='.') :marked .alert.is-helpful :marked This small test demonstrates how Angular tests can verify a component's visual representation — something not possible with [isolated unit tests](#isolated-component-tests) — at low cost and without resorting to much slower and more complicated end-to-end tests. :marked The second test verifies click behavior. Clicking the hero should raise a `selected` event that the host component (`DashboardComponent` presumably) can hear: +makeExample('testing/ts/app/dashboard/dashboard-hero.component.spec.ts', 'click-test', 'app/dashboard/dashboard-hero.component.spec.ts (click test)')(format='.') :marked The component exposes an `EventEmitter` property. The test subscribes to it just as the host component would do. The Angular `DebugElement.triggerEventHandler` lets the test raise _any data-bound event_. In this example, the component's template binds to the hero `
`. The test has a reference to that `
` in `heroEl` so triggering the `heroEl` click event should cause Angular to call `DashboardHeroComponent.click`. If the component behaves as expected, its `selected` property should emit the `hero` object, the test detects that emission through its subscription, and the test will pass. .l-hr #component-inside-test-host :marked # Test a component inside a test host component In the previous approach the tests themselves played the role of the host `DashboardComponent`. A nagging suspicion remains. Will the `DashboardHeroComponent` work properly when properly data-bound to a host component? Testing with the actual `DashboardComponent` host is doable but seems more trouble than its worth. It's easier to emulate the `DashboardComponent` host with a _test host_ like this one: +makeExample('testing/ts/app/dashboard/dashboard-hero.component.spec.ts', 'test-host', 'app/dashboard/dashboard-hero.component.spec.ts (test host)')(format='.') :marked The test host binds to `DashboardHeroComponent` as the `DashboardComponent` would but without the distraction of the `Router`, the `HeroService` or even the `*ngFor` repeater. The test host sets the component's `hero` input property with its test hero. It binds the component's `selected` event with its `onSelected` handler that records the emitted hero in its `selectedHero` property. Later the tests check that property to verify that the `DashboardHeroComponent.selected` event really did emit the right hero. The setup for the test-host tests is similar to the setup for the stand-alone tests: +makeExample('testing/ts/app/dashboard/dashboard-hero.component.spec.ts', 'test-host-setup', 'app/dashboard/dashboard-hero.component.spec.ts (test host setup)')(format='.') :marked This testing module configuration shows two important differences: 1. It _declares_ both the `DashboardHeroComponent` and the `TestHostComponent`. 1. It _creates_ the `TestHostComponent` instead of the `DashboardHeroComponent`. The `fixture` returned by `createComponent` holds an instance of `TestHostComponent` instead of an instance of `DashboardHeroComponent`. Of course creating the `TestHostComponent` has the side-effect of creating a `DashboardHeroComponent` because the latter appears within the template of the former. The query for the hero element (`heroEl`) still finds it in the test DOM albeit at greater depth in the element tree than before. The tests themselves are almost identical to the stand-alone version +makeExample('testing/ts/app/dashboard/dashboard-hero.component.spec.ts', 'test-host-tests', 'app/dashboard/dashboard-hero.component.spec.ts (test-host)')(format='.') :marked Only the selected event test differs. It confirms that the selected `DashboardHeroComponent` hero really does find its way up through the event binding to the host component. a(href="#top").to-top Back to top .l-hr #routed-component :marked # Test a routed component Testing the actual `DashboardComponent` seemed daunting because it injects the `Router`. +makeExample('testing/ts/app/dashboard/dashboard.component.ts', 'ctor', 'app/dashboard/dashboard.component.ts (constructor)')(format='.') :marked It also injects the `HeroService` but faking that is a [familiar story](#component-with-async-servic). The `Router` has a complicated API and is entwined with other services and application pre-conditions. Fortunately, the `DashboardComponent` isn't doing much with the `Router` +makeExample('testing/ts/app/dashboard/dashboard.component.ts', 'goto-detail', 'app/dashboard/dashboard.component.ts (goToDetail)')(format='.') :marked This is often the case. As a rule you test the component, not the router, and care only if the component navigates with the right address under the given conditions. Stubbing the router with a test implementation is an easy option. This should do the trick: +makeExample('testing/ts/app/dashboard/dashboard.component.spec.ts', 'router-stub', 'app/dashboard/dashboard.component.spec.ts (Router Stub)')(format='.') :marked Now we setup the testing module with the test stubs for the `Router` and `HeroService` and create a test instance of the `DashbaordComponent` for subsequent testing. +makeExample('testing/ts/app/dashboard/dashboard.component.spec.ts', 'compile-and-create-body', 'app/dashboard/dashboard.component.spec.ts (compile and create)')(format='.') :marked The following test clicks the displayed hero and confirms (with the help of a spy) that `Router.navigateByUrl` is called with the expected url. +makeExample('testing/ts/app/dashboard/dashboard.component.spec.ts', 'navigate-test', 'app/dashboard/dashboard.component.spec.ts (navigate test)')(format='.') #inject :marked ## The _inject_ function Notice the `inject` function in the second `it` argument. +makeExample('testing/ts/app/dashboard/dashboard.component.spec.ts', 'inject')(format='.') :marked The `inject` function is an independent feature of the _Angular Testing Platform_. It injects services into the test function where you can alter, spy on, and manipulate them. The `inject` function has two parameters 1. an array of Angular dependency injection tokens 1. a test function whose parameters correspond exactly to each item in the injection token array .callout.is-important header inject uses the TestBed Injector :marked The `inject` function uses the current `TestBed` injector and can only return services provided at that level. It does not return services from component providers. :marked This example injects the `Router` from the current `TestBed` injector. That's fine for this test because the `Router` is (and must be) provided by the application root injector. If you need a service provided by the component's _own_ injector, call `fixture.debugElement.injector.get` instead: +makeExample('testing/ts/app/welcome.component.spec.ts', 'injected-service', 'Component\'s injector')(format='.') .alert.is-important :marked Use the component's own injector to get the service actually injected into the component. :marked The `inject` function closes the current `TestBed` instance to further configuration. You cannot call any more `TestBed` configuration methods, not `configureTestModule` nor any of the `override...` methods. The `TestBed` throws an error if you try. .alert.is-important :marked Do not configure the `TestBed` after calling `inject`. a(href="#top").to-top Back to top .l-hr #routed-component-w-param :marked # Test a routed component with parameters Clicking a _Dashboard_ hero triggers navigation to `heroes/:id` where `:id` is a route parameter whose value is the `id` of the hero to edit. That URL matches a route to the `HeroDetailComponent`. The router pushes the `:id` token value into the `ActivatedRoute.params` _Observable_ property, Angular injects the `ActivatedRoute` into the `HeroDetailComponent`, and the component extracts the `id` so it can fetch the corresponding hero via the `HeroDetailService`. Here's the `HeroDetailComponent` constructor: +makeExample('testing/ts/app/hero/hero-detail.component.ts', 'ctor', 'app/hero/hero-detail.component.ts (constructor)')(format='.') :marked `HeroDetailComponent` listens for changes to the `ActivatedRoute.params` in its `ngOnInit` method. +makeExample('testing/ts/app/hero/hero-detail.component.ts', 'ng-on-init', 'app/hero/hero-detail.component.ts (ngOnInit)')(format='.') .l-sub-section :marked The expression after `route.params` chains an _Observable_ operator that _plucks_ the `id` from the `params` and then chains a `forEach` operator to subscribes to `id`-changing events. The `id` changes every time the user navigates to a different hero. The `forEach` passes the new `id` value to the component's `getHero` method (not shown) which fetches a hero and sets the component's `hero` property. If the`id` parameter is missing, the `pluck` operator fails and the `catch` treats failure as a request to edit a new hero. The [Router](router.html#route-parameters) chapter covers `ActivatedRoute.params` in more detail. :marked A test can explore how the `HeroDetailComponent` responds to different `id` parameter values by manipulating the `ActivatedRoute` injected into the component's constructor. By now you know how to stub the `Router` and a data service. Stubbing the `ActivatedRoute` would follow the same pattern except for a complication: the `ActivatedRoute.params` is an _Observable_. #stub-observable :marked ### _Observable_ test double The `hero-detail.component.spec.ts` relies on an `ActivatedRouteStub` to set `ActivatedRoute.params` values for each test. This is a cross-application, re-usable _test helper class_. We recommend locating such helpers in a `testing` folder sibling to the `app` folder. This sample keeps `ActivatedRouteStub` in `testing/router-stubs.ts`: +makeExample('testing/ts/testing/router-stubs.ts', 'activated-route-stub', 'testing/router-stubs.ts (ActivatedRouteStub)')(format='.') :marked Notable features of this stub: * The stub implements only two of the `ActivatedRoute` capabilities: `params` and `snapshot.params`. * _BehaviorSubject_ drives the stub's `params` _Observable_ and returns the same value to every `params` subscriber until it's given a new value. * The `HeroDetailComponent` chain its expressions to this stub `params` _Observable_ which is now under the tester's control. * Setting the `testParams` property causes the `subject` to push the assigned value into `params`. That triggers the `HeroDetailComponent` _params_ subscription, described above, in the same way that navigation does. * Setting the `testParams` property also updates the stub's internal value for the `snapshot` property to return. .l-sub-section(style="margin-left:30px") :marked The [_snapshot_](router.html#snapshot "Router Chapter: snapshot") is another popular way for components to consume route parameters. .callout.is-helpful :marked The router stubs in this chapter are meant to inspire you. Create your own stubs to fit your testing needs. #observable-tests :marked ### _Observable_ tests Here's a test demonstrating the component's behavior when the observed `id` refers to an existing hero: +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'route-good-id', 'app/hero/hero-detail.component.spec.ts (existing id)')(format='.') .l-sub-section :marked The `createComponent` method and `page` object are discussed [in the next section](#page-object). Rely on your intuition for now. :marked When the `id` cannot be found, the component should re-route to the `HeroListComponent`. The test suite setup provided the same `RouterStub` [described above](#routed-component) which spies on the router without actually navigating. This test supplies a "bad" id and expects the component to try to navigate. +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'route-bad-id', 'app/hero/hero-detail.component.spec.ts (bad id)')(format='.') :marked :marked While this app doesn't have a route to the `HeroDetailComponent` that omits the `id` parameter, it might add such a route someday. The component should do something reasonable when there is no `id`. In this implementation, the component should create and display a new hero. New heroes have `id=0` and a blank `name`. This test confirms that the component behaves as expected: +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'route-no-id', 'app/hero/hero-detail.component.spec.ts (no id)')(format='.') :marked .callout.is-helpful :marked Inspect and download _all_ of the chapter's application test code with this live example. .l-hr #page-object :marked # Use a _page_ object to simplify setup The `HeroDetailComponent` is a simple view with a title, two hero fields, and two buttons. figure.image-display img(src='/resources/images/devguide/testing/hero-detail.component.png' alt="HeroDetailComponent in action") :marked But there's already plenty of template complexity. +makeExample('testing/ts/app/hero/hero-detail.component.html', '', 'app/hero/hero-detail.component.html')(format='.') :marked To fully exercise the component, the test needs ... * to wait until a `hero` arrives before `*ngIf` allows any element in DOM * element references for the title name span and name input-box to inspect their values * two button references to click * spies on services and component methods Even a small form such as this one can produce a mess of tortured conditional setup and CSS element selection. Tame the madness with a `Page` class that simplifies access to component properties and encapsulates the logic that sets them. Here's the `Page` class for the `hero-detail.component.spec.ts` +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'page', 'app/hero/hero-detail.component.spec.ts (Page)')(format='.') :marked Now the important hooks for component manipulation and inspection are neatly organized and accessible from an instance of `Page`. A `createComponent` method creates a `page` and fills in the blanks once the `hero` arrives. +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'create-component', 'app/hero/hero-detail.component.spec.ts (createComponent)')(format='.') :marked The [observable tests](#observable-tests) in the previous section demonstrate how `createComponent` and `page` keep the tests short and _on message_. There are no distractions: no waiting for promises to resolve and no searching the DOM for element values to compare. Here are a few more `HeroDetailComponent` tests to drive the point home. +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'selected-tests', 'app/hero/hero-detail.component.spec.ts (selected tests)')(format='.') a(href="#top").to-top Back to top .l-hr #import-module :marked # Setup with module imports Earlier component tests configured the testing module with a few `declarations` like this: +makeExample('testing/ts/app/dashboard/dashboard-hero.component.spec.ts', 'compile-components', 'app/dashboard/dashboard-hero.component.spec.ts (config)')(format='.') :marked The `DashboardComponent` is simple. It needs no help. But more complex components often depend on other components, directives, pipes, and providers and these must be added to the testing module too. Fortunately, the `TestBed.configureTestingModule` parameter parallels the metadata passed to the `@NgModule` decorator which means you can also specify `providers` and `imports. The `HeroDetailComponent` requires a lot of help despite its small size and simple construction. In addition to the support it receives from the default testing module `CommonModule`, it needs: * `NgModel` and friends in the `FormsModule` enable two-way data binding * The `TitleCasePipe` from the `shared` folder * Router services (which these tests are stubbing) * Hero data access services (also stubbed) One approach is to configure the testing module from the individual pieces as in this example: +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'setup-forms-module', 'app/hero/hero-detail.component.spec.ts (FormsModule setup)')(format='.') :marked Because many app components need the `FormsModule` and the `TitleCasePipe`, the developer created a `SharedModule` to combine these and other frequently requested parts. The test configuration can use the `SharedModule` too as seen in this alternative setup: +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'setup-shared-module', 'app/hero/hero-detail.component.spec.ts (SharedModule setup)')(format='.') :marked It's a bit tighter and smaller, with fewer import statements (not shown). #feature-module-import :marked ### Import the feature module The `HeroDetailComponent` is part of the `HeroModule` [Feature Module](ngmodule.html#feature-modules) that aggregates more of the interdependent pieces including the `SharedModule`. Try a test configuration that imports the `HeroModule` like this one: +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'setup-hero-module', 'app/hero/hero-detail.component.spec.ts (HeroModule setup)')(format='.') :marked That's _really_ crisp. Only the _test doubles_ in the `providers` remain. Even the `HeroDetailComponent` declaration is gone. .l-sub-section :marked In fact, if you try to declare it, Angular throws an error because `HeroDetailComponent` is declared in both the `HeroModule` and the `DynamicTestModule` (the testing module). .alert.is-helpful :marked Importing the component's feature module is often the easiest way to configure the tests, especially when the feature module is small and mostly self-contained ... as feature modules should be. :marked a(href="#top").to-top Back to top .l-hr #component-override :marked # Override component providers The `HeroDetailComponent` provides its own `HeroDetailService`. +makeExample('testing/ts/app/hero/hero-detail.component.ts', 'prototype', 'app/hero/hero-detail.component.ts (prototype)')(format='.') :marked It's not possible to stub the component's `HeroDetailService` in the `providers` of the `TestBed.configureTestingModule`. Those are providers for the _testing module_, not the component. They prepare the dependency injector at the _fixture level_. Angular creates the component with its _own_ injector which is a _child_ of the fixture injector. It registers the component's providers (the `HeroDetailService` in this case) with the child injector. A test cannot get to child injector services from the fixture injector. And `TestBed.configureTestingModule` can't configure them either. Angular has been creating new instances of the real `HeroDetailService` all along! .l-sub-section :marked These tests could fail or timeout if the `HeroDetailService` made its own XHR calls to a remote server. There might not be a remote server to call. Fortunately, the `HeroDetailService` delegates responsibility for remote data access to an injected `HeroService`. +makeExample('testing/ts/app/hero/hero-detail.service.ts', 'prototype', 'app/hero/hero-detail.service.ts (prototype)')(format='.') :marked The [previous test configuration](#feature-module-import) replaces the real `HeroService` with a `FakeHeroService` that intercepts server requests and fakes their responses. :marked What if you aren't so lucky. What if faking the `HeroService` is hard? What if `HeroDetailService` makes its own server requests? The `TestBed.overrideComponent` method can replace the component's `providers` with easy-to-manage _test doubles_ as seen in the following setup variation: +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'setup-override', 'app/hero/hero-detail.component.spec.ts (Override setup)')(format='.') :marked Notice that `TestBed.configureTestingModule` no longer provides a (fake) `HeroService` because it's [not needed](#stub-hero-detail-service). #override-component-method :marked ### The _overrideComponent_ method Focus on the `overrideComponent` method. +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'override-component-method', 'app/hero/hero-detail.component.spec.ts (overrideComponent)')(format='.') :marked It takes two arguments: the component type to override (`HeroDetailComponent`) and an override metadata object. The [overide metadata object](#metadata-override-object) is a generic defined as follows: code-example(format="." language="javascript"). type MetadataOverride = { add?: T; remove?: T; set?: T; }; :marked A metadata override object can either add-and-remove elements in metadata properties or completely reset those properties. This example resets the component's `providers` metadata. The type parameter, `T`, is the kind of metadata you'd pass to the `@Component` decorator: code-example(format="." language="javascript"). selector?: string; template?: string; templateUrl?: string; providers?: any[]; ... #stub-hero-detail-service :marked ### _StubHeroDetailService_ This example completely replaces the component's `providers` with an array containing the `StubHeroDetailService`. The `StubHeroDetailService` is dead simple. It doesn't need a `HeroService` (fake or otherwise). +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'stub-hds', 'app/hero/hero-detail.component.spec.ts (StubHeroDetailService)')(format='.') :marked ### The override tests Now the tests can control the component's hero directly by manipulating the stub's `testHero`. +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'override-tests', 'app/hero/hero-detail.component.spec.ts (override tests)')(format='.') :marked ### More overrides The `TestBed.overrideComponent` method can be called multiple times for the same or different components. The `TestBed` offers similar `overrideDirective`, `overrideModule`, and `overridePipe` methods for digging into and replacing parts of these other classes. Explore the options and combinations on your own. a(href="#top").to-top Back to top .l-hr #router-outlet-component :marked # Test a _RouterOutlet_ component The `AppComponent` displays routed components in a ``. It also displays a navigation bar with anchors and their `RouterLink` directives. #app-component-html +makeExample('testing/ts/app/app.component.html', '', 'app/app.component.html')(format='.') :marked The component class does nothing. +makeExample('testing/ts/app/app.component.ts', '', 'app/app.component.ts')(format='.') :marked Unit tests can confirm that the anchors are wired properly without engaging the router. See why this is worth doing [below](#why-stubbed-routerlink-tests). #stub-component :marked ### Stubbing unneeded components The test setup should look familiar +makeExample('testing/ts/app/app.component.spec.ts', 'setup-stubs', 'app/app.component.spec.ts (Stub Setup)')(format='.') :marked The `AppComponent` is the declared test subject The setup extends the default testing module with one real component (`BannerComponent`) and several stubs. * `BannerComponent` is simple and harmless to use as is. * The real `WelcomeComponent` has an injected service. `WelcomeStubComponent` is a placeholder with no service to worry about. * The real `RouterOutlet` is complex and errors easily. The `RouterOutletStubComponent` (in `testing/router-stubs.ts`) is safely inert. The component stubs are essential. Without them, the Angular compiler doesn't recognize the `` and `` tags and throws an error. #router-link-stub :marked ### Stubbing the _RouterLink_ The `RouterLinkStubDirective` contributes substantively to the test +makeExample('testing/ts/testing/router-stubs.ts', 'router-link', 'testing/router-stubs.ts (RouterLinkStubDirective)')(format='.') :marked The `host` metadata property wires the click event of the host element (the ``) to the directive's `onClick` method. The URL bound to the `[routerLink]` attribute flows to the directive's `linkParams` property. Clicking the anchor should trigger the `onClick` method which sets the telltale `navigatedTo` property. Tests can inspect that property to confirm the expected _click-to-navigation_ behavior. #by-directive #inject-directive :marked ### _By.directive_ and injected directives A little more setup triggers the initial data binding and gets references to the navigation links: +makeExample('testing/ts/app/app.component.spec.ts', 'test-setup', 'app/app.component.spec.ts (test setup)')(format='.') :marked Two points of special interest: 1. You can locate elements _by directive_, using `By.directive`, not just by css selectors. 1. You can use the component's dependency injector to get an attached directive because Angular always adds attached directives to the component's injector. #app-component-tests :marked Here are some tests that leverage this setup: +makeExample('testing/ts/app/app.component.spec.ts', 'tests', 'app/app.component.spec.ts (selected tests)')(format='.') .l-sub-section :marked The "click" test _in this example_ is worthless. It works hard to appear useful when in fact it tests the `RouterLinkStubDirective` rather than the _component_. This is a common failing of directive stubs. It has a legitimate purpose in this chapter. It demonstrates how to find a `RouterLink` element, click it, and inspect a result, without engaging the full router machinery. This is a skill you may need to test a more sophisticated component, one that changes the display, re-calculates parameters, or re-arranges navigation options when the user clicks the link. #why-stubbed-routerlink-tests :marked ### What good are these tests? Stubbed `RouterLink` tests can confirm that a component with links and an outlet is setup properly, that the component has the links it should have, and that they are all pointing in the expected direction. These tests do not concern whether the app will succeed in navigating to the target component when the user clicks a link. Stubbing the RouterLink and RouterOutlet is the best option for such limited testing goals. Relying on the real router would make them brittle. They could fail for reasons unrelated to the component. For example, a navigation guard could prevent an unauthorized user from visiting the `HeroListComponent`. That's not the fault of the `AppComponent` and no change to that component could cure the failed test. A _different_ battery of tests can explore whether the application navigates as expected in the presence of conditions that influence guards such as whether the user is authenticated and authorized. A future chapter update will explain how to write such tests with the `RouterTestingModule`. a(href="#top").to-top Back to top .l-hr #shallow-component-test :marked # "Shallow component tests" with *NO\_ERRORS\_SCHEMA* The [previous setup](#stub-component) declared the `BannerComponent` and stubbed two other components for _no reason other than to avoid a compiler error_. Without them, the Angular compiler doesn't recognize the ``, `` and `` tags in the [_app.component.html_](#app-component-html) template and throws an error. Add `NO_ERRORS_SCHEMA` to the testing module's `schemas` metadata to tell the compiler to ignore unrecognized elements and attributes. You no longer have to declare irrelevant components and directives. These tests are ***shallow*** because they only "go deep" into the components you want to test. Here is a setup (with `import` statements) that demonstrates the improved simplicity of _shallow_ tests, relative to the stubbing setup. +makeTabs('testing/ts/app/app.component.spec.ts, testing/ts/app/app.component.spec.ts', 'setup-schemas, setup-stubs-w-imports', 'app/app.component.spec.ts (NO_ERRORS_SCHEMA), app/app.component.spec.ts (Stubs)')(format='.') :marked The _only_ declarations are the _component-under-test_ (`AppComponent`) and the `RouterLinkStubDirective` that contributes actively to the tests. The [tests in this example](#app-component-tests) are unchanged. .alert.is-important :marked _Shallow component tests_ with `NO_ERRORS_SCHEMA` greatly simplify unit testing of complex templates. However, the compiler no longer alerts you to mistakes such as misspelled or misused components and directives. a(href="#top").to-top Back to top .l-hr #attribute-directive :marked # Test an attribute directive An _attribute directive_ modifies the behavior of an element, component or another directive. Its name reflects the way the directive is applied: as an attribute on a host element. The sample application's `HighlightDirective` sets the background color of an element based on either a data bound color or a default color (lightgray). It also sets a custom property of the element (`customProperty`) to `true` for no reason other than to show that it can. +makeExample('testing/ts/app/shared/highlight.directive.ts', '', 'app/shared/highlight.directive.ts')(format='.') :marked It's used throughout the application, perhaps most simply in the `AboutComponent`: +makeExample('testing/ts/app/about.component.ts', '', 'app/about.component.ts')(format='.') :marked Testing the specific use of the `HighlightDirective` within the `AboutComponent` requires only the techniques explored above (in particular the ["Shallow test"](#shallow-component-test) approach). +makeExample('testing/ts/app/about.component.spec.ts', 'tests', 'app/about.component.spec.ts')(format='.') :marked However, testing a single use case is unlikely to explore the full range of a directive's capabilities. Finding and testing all components that use the directive is tedious, brittle, and almost as unlikely to afford full coverage. [Isolated unit tests](#isolated-tests) might be helpful. But attribute directives like this one tend to manipulate the DOM. Isolated tests don't and therefore don't inspire confidence in the directive's efficacy. A better solution is to create an artificial test component that demonstrates all ways to apply the directive. +makeExample('testing/ts/app/shared/highlight.directive.spec.ts', 'test-component', 'app/shared/highlight.directive.spec.ts (test component)')(format='.') figure.image-display img(src='/resources/images/devguide/testing/highlight-directive-spec.png' width="200px" alt="HighlightDirective spec in action") .l-sub-section :marked The `` case binds the `HighlightDirective` to the name of a color value in the input box. The initial value is the word "cyan" which should be the background color of the input box. :marked Here are some tests of this component: +makeExample('testing/ts/app/shared/highlight.directive.spec.ts', 'selected-tests', 'app/shared/highlight.directive.spec.ts (selected tests)') :marked A few techniques are noteworthy: * The `By.directive` predicate is a great way to get the elements that have this directive _when their element types are unknown_. * The `:not` pseudo-class in `By.css('h2:not([highlight])')` helps find `

` elements that _do not_ have the directive. `By.css('*:not([highlight])')` finds _any_ element that does not have the directive. * `DebugElement.styles` affords access to element styles even in the absence of a real browser, thanks to the `DebugElement` abstraction. But feel free to exploit the `nativeElement` when that seems easier or more clear than the abstraction. * Angular adds a directive to the injector of the element to which it is applied. The test for the default color uses the injector of the 2nd `

` to get its `HighlightDirective` instance and its `defaultColor`. * `DebugElement.properties` affords access to the artificial custom property that is set by the directive. a(href="#top").to-top Back to top .l-hr #isolated-tests #testing-without-atp :marked # Testing without the Angular Testing Platform Testing applications with the help of the Angular Testing Platform (ATP) is the main focus of this chapter. However, it's often more productive to explore the inner logic of application classes with _isolated_ unit tests that don't use the ATP. Such tests are often smaller, easier to read, and easier to write and maintain. They don't * import from the Angular test libraries * configure a module * prepare dependency injection `providers` * call `inject` or `async` or `fakeAsync` They do * exhibit standard, Angular-agnostic testing techniques * create instances directly with `new` * substitute test doubles (stubs, spys, and mocks) for the real dependencies. .callout.is-important header Write both kinds of tests :marked Good developers write both kinds of tests for the same application part, often in the same spec file. Write simple _isolated_ unit tests to validate the part in isolation. Write _Angular_ tests to validate the part as it interacts with Angular, updates the DOM, and collaborates with the rest of the application. #isolated-service-tests :marked ## Services Services are good candidates for isolated unit testing. Here are some synchronous and asynchronous unit tests of the `FancyService` written without assistance from Angular Testing Platform. +makeExample('testing/ts/app/bag/bag.no-testbed.spec.ts', 'FancyService', 'app/bag/bag.no-testbed.spec.ts') :marked A rough line count suggests that these tests are about 25% smaller than equivalent ATP tests. That's telling but not decisive. The benefit comes from reduced setup and code complexity. Compare these equivalent tests of `FancyService.getTimeoutValue`. +makeTabs( `testing/ts/app/bag/bag.no-testbed.spec.ts, testing/ts/app/bag/bag.spec.ts`, 'getTimeoutValue, getTimeoutValue', `app/bag/bag.no-testbed.spec.ts, app/bag/bag.spec.ts (with ATP)`) :marked They have about the same line-count. The ATP version has more moving parts, including a couple of helper functions (`async` and `inject`). Both work and it's not much of an issue if you're using the Angular Testing Platform nearby for other reasons. On the other hand, why burden simple service tests with ATP complexity? Pick the approach that suits you. ### Services with dependencies Services often depend on other services that Angular injects into the constructor. You can test these services _without_ the testbed. In many cases, it's easier to create and _inject_ dependencies by hand. The `DependentService` is a simple example +makeExample('testing/ts/app/bag/bag.ts', 'DependentService', 'app/bag/bag.ts')(format='.') :marked It delegates it's only method, `getValue`, to the injected `FancyService`. Here are several ways to test it. +makeExample('testing/ts/app/bag/bag.no-testbed.spec.ts', 'DependentService', 'app/bag/bag.no-testbed.spec.ts') :marked The first test creates a `FancyService` with `new` and passes it to the `DependentService` constructor. It's rarely that simple. The injected service can be difficult to create or control. You can mock the dependency, or use a dummy value, or stub the pertinent service method with a substitute method that is easy to control. These _isolated_ unit testing techniques are great for exploring the inner logic of a service or its simple integration with a component class. Use the Angular Testing Platform when writing tests that validate how a service interacts with components _within the Angular runtime environment_. #isolated-pipe-tests :marked ## Pipes Pipes are easy to test without the Angular Testing Platform (ATP). A pipe class has one method, `transform`, that turns an input to an output. The `transform` implementation rarely interacts with the DOM. Most pipes have no dependence on Angular other than the `@Pipe` metadata and an interface. Consider a `TitleCasePipe` that capitalizes the first letter of each word. Here's a naive implementation implemented with a regular expression. +makeExample('testing/ts/app/shared/title-case.pipe.ts', '', 'app/shared/title-case.pipe.ts')(format='.') :marked Anything that uses a regular expression is worth testing thoroughly. Use simple Jasmine to explore the expected cases and the edge cases. +makeExample('testing/ts/app/shared/title-case.pipe.spec.ts', 'excerpt', 'app/shared/title-case.pipe.spec.ts') :marked ### Write ATP tests too These are tests of the pipe _in isolation_. They can't tell if the `TitleCasePipe` is working properly as applied in the application components. Consider adding ATP component tests such as this one. +makeExample('testing/ts/app/hero/hero-detail.component.spec.ts', 'title-case-pipe', 'app/hero/hero-detail.component.spec.ts (pipe test)') #isolated-component-tests :marked ## Components Component tests typically examine how a component class interacts with its own template or with collaborating components. The Angular Testing Platform is specifically designed to facilitate such tests. Consider this `ButtonComp` component. +makeExample('testing/ts/app/bag/bag.ts', 'ButtonComp', 'app/bag/bag.ts (ButtonComp)')(format='.') :marked The following ATP test demonstrates that clicking a button in the template leads to an update of the on-screen message. +makeExample('testing/ts/app/bag/bag.spec.ts', 'ButtonComp', 'app/bag/bag.spec.ts (ButtonComp)')(format='.') :marked The assertions verify the data binding flow from one HTML control (the `