diff --git a/.pullapprove.yml b/.pullapprove.yml index 057d0b9615..43774920b4 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -612,6 +612,14 @@ groups: contains_any_globs(files.exclude('packages/compiler-cli/**'), [ '**/testing/**', 'aio/content/guide/testing.md', + 'aio/content/guide/test-debugging.md', + 'aio/content/guide/testing-attribute-directives.md', + 'aio/content/guide/testing-code-coverage.md', + 'aio/content/guide/testing-components-basics.md', + 'aio/content/guide/testing-components-scenarios.md', + 'aio/content/guide/testing-pipes.md', + 'aio/content/guide/testing-services.md', + 'aio/content/guide/testing-utility-apis.md', 'aio/content/examples/testing/**', 'aio/content/images/guide/testing/**' ]) diff --git a/aio/content/guide/test-debugging.md b/aio/content/guide/test-debugging.md new file mode 100644 index 0000000000..331ed45943 --- /dev/null +++ b/aio/content/guide/test-debugging.md @@ -0,0 +1,28 @@ +# Debugging tests + +If your tests aren't working as you expect them to, you can inspect and debug them in the browser. + +
+ + For the sample app that the testing guides describe, see the sample app. + + For the tests features in the testing guides, see tests. + +
+ + +Debug specs in the browser in the same way that you debug an application. + +1. Reveal the Karma browser window. See [Set up testing](guide/testing#set-up-testing) if you need help with this step. +1. Click the **DEBUG** button; it opens a new browser tab and re-runs the tests. +1. Open the browser's “Developer Tools” (`Ctrl-Shift-I` on Windows; `Command-Option-I` in macOS). +1. Pick the "sources" section. +1. Open the `1st.spec.ts` test file (Control/Command-P, then start typing the name of the file). +1. Set a breakpoint in the test. +1. Refresh the browser, and it stops at the breakpoint. + + + +
diff --git a/aio/content/guide/testing-attribute-directives.md b/aio/content/guide/testing-attribute-directives.md new file mode 100644 index 0000000000..3c704da745 --- /dev/null +++ b/aio/content/guide/testing-attribute-directives.md @@ -0,0 +1,78 @@ + +{@a attribute-directive} + +# Testing Attribute Directives + +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. + +
+ + For the sample app that the testing guides describe, see the sample app. + + For the tests features in the testing guides, see tests. + +
+ +## Testing the `HighlightDirective` + +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. + + + +It's used throughout the application, perhaps most simply in the `AboutComponent`: + + + +Testing the specific use of the `HighlightDirective` within the `AboutComponent` requires only the techniques explored in the ["Nested component tests"](guide/testing-components-scenarios#nested-component-tests) section of [Component testing scenarios](guide/testing-components-scenarios). + + + +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. + +_Class-only tests_ might be helpful, +but attribute directives like this one tend to manipulate the DOM. +Isolated unit tests don't touch the DOM and, therefore, +do not 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. + + + + + +
+ +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. + +
+ +Here are some tests of this component: + + + +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 second `

` to get its `HighlightDirective` instance + and its `defaultColor`. + +- `DebugElement.properties` affords access to the artificial custom property that is set by the directive. + +
diff --git a/aio/content/guide/testing-code-coverage.md b/aio/content/guide/testing-code-coverage.md new file mode 100644 index 0000000000..d115544d46 --- /dev/null +++ b/aio/content/guide/testing-code-coverage.md @@ -0,0 +1,57 @@ +{@a code-coverage} + +# Find out how much code you're testing + +The CLI can run unit tests and create code coverage reports. +Code coverage reports show you any parts of your code base that may not be properly tested by your unit tests. + +
+ + For the sample app that the testing guides describe, see the sample app. + + For the tests features in the testing guides, see tests. + +
+ + +To generate a coverage report run the following command in the root of your project. + + + ng test --no-watch --code-coverage + + +When the tests are complete, the command creates a new `/coverage` folder in the project. Open the `index.html` file to see a report with your source code and code coverage values. + +If you want to create code-coverage reports every time you test, you can set the following option in the CLI configuration file, `angular.json`: + +``` + "test": { + "options": { + "codeCoverage": true + } + } +``` + +## Code coverage enforcement + +The code coverage percentages let you estimate how much of your code is tested. +If your team decides on a set minimum amount to be unit tested, you can enforce this minimum with the Angular CLI. + +For example, suppose you want the code base to have a minimum of 80% code coverage. +To enable this, open the [Karma](https://karma-runner.github.io) test platform configuration file, `karma.conf.js`, and add the following in the `coverageIstanbulReporter:` key. + +``` +coverageIstanbulReporter: { + reports: [ 'html', 'lcovonly' ], + fixWebpackSourcePaths: true, + thresholds: { + statements: 80, + lines: 80, + branches: 80, + functions: 80 + } +} +``` + +The `thresholds` property causes the tool to enforce a minimum of 80% code coverage when the unit tests are run in the project. + diff --git a/aio/content/guide/testing-components-basics.md b/aio/content/guide/testing-components-basics.md new file mode 100644 index 0000000000..03fcb3c53b --- /dev/null +++ b/aio/content/guide/testing-components-basics.md @@ -0,0 +1,380 @@ +# Basics of testing components + +A component, unlike all other parts of an Angular application, +combines an HTML template and a TypeScript class. +The component truly is the template and the class _working together_. To adequately test a component, you should test that they work together +as intended. + +Such tests require creating the component's host element in the browser DOM, +as Angular does, and investigating the component class's interaction with +the DOM as described by its template. + +The Angular `TestBed` facilitates this kind of testing as you'll see in the sections below. +But in many cases, _testing the component class alone_, without DOM involvement, +can validate much of the component's behavior in an easier, more obvious way. + +
+ + For the sample app that the testing guides describe, see the sample app. + + For the tests features in the testing guides, see tests. + +
+ + +{@a component-class-testing} + +## Component class testing + +Test a component class on its own as you would test a service class. + +Component class testing should be kept very clean and simple. +It should test only a single unit. +At first glance, you should be able to understand +what the test is testing. + +Consider this `LightswitchComponent` which toggles a light on and off +(represented by an on-screen message) when the user clicks the button. + + + +You might decide only to test that the `clicked()` method +toggles the light's _on/off_ state and sets the message appropriately. + +This component class has no dependencies. To test these types of classes, follow the same steps as you would for a service that has no dependencies: + +1. Create a component using the new keyword. +2. Poke at its API. +3. Assert expectations on its public state. + + + +Here is the `DashboardHeroComponent` from the _Tour of Heroes_ tutorial. + + + +It appears within the template of a parent component, +which binds a _hero_ to the `@Input` property and +listens for an event raised through the _selected_ `@Output` property. + +You can test that the class code works without creating the `DashboardHeroComponent` +or its parent component. + + + +When a component has dependencies, you may wish to use the `TestBed` to both +create the component and its dependencies. + +The following `WelcomeComponent` depends on the `UserService` to know the name of the user to greet. + + + +You might start by creating a mock of the `UserService` that meets the minimum needs of this component. + + + +Then provide and inject _both the_ **component** _and the service_ in the `TestBed` configuration. + + + +Then exercise the component class, remembering to call the [lifecycle hook methods](guide/lifecycle-hooks) as Angular does when running the app. + + + +## Component DOM testing + +Testing the component _class_ is as easy as [testing a service](guide/testing-services). + +But a component is more than just its class. +A component interacts with the DOM and with other components. +The _class-only_ tests can tell you about class behavior. +They cannot tell you if the component is going to render properly, +respond to user input and gestures, or integrate with its parent and child components. + +None of the _class-only_ tests above can answer key questions about how the +components actually behave on screen. + +- Is `Lightswitch.clicked()` bound to anything such that the user can invoke it? +- Is the `Lightswitch.message` displayed? +- Can the user actually select the hero displayed by `DashboardHeroComponent`? +- Is the hero name displayed as expected (i.e, in uppercase)? +- Is the welcome message displayed by the template of `WelcomeComponent`? + +These may not be troubling questions for the simple components illustrated above. +But many components have complex interactions with the DOM elements +described in their templates, causing HTML to appear and disappear as +the component state changes. + +To answer these kinds of questions, you have to create the DOM elements associated +with the components, you must examine the DOM to confirm that component state +displays properly at the appropriate times, and you must simulate user interaction +with the screen to determine whether those interactions cause the component to +behave as expected. + +To write these kinds of test, you'll use additional features of the `TestBed` +as well as other testing helpers. + +### CLI-generated tests + +The CLI creates an initial test file for you by default when you ask it to +generate a new component. + +For example, the following CLI command generates a `BannerComponent` in the `app/banner` folder (with inline template and styles): + + +ng generate component banner --inline-template --inline-style --module app + + +It also generates an initial test file for the component, `banner-external.component.spec.ts`, that looks like this: + + + +
+ +Because `compileComponents` is asynchronous, it uses +the [`async`](api/core/testing/async) utility +function imported from `@angular/core/testing`. + +Please refer to the [async](guide/testing-components-scenarios#async) section for more details. + +
+ +### Reduce the setup + +Only the last three lines of this file actually test the component +and all they do is assert that Angular can create the component. + +The rest of the file is boilerplate setup code anticipating more advanced tests that _might_ become necessary if the component evolves into something substantial. + +You'll learn about these advanced test features below. +For now, you can radically reduce this test file to a more manageable size: + + + +In this example, the metadata object passed to `TestBed.configureTestingModule` +simply declares `BannerComponent`, the component to test. + + + + +
+ +There's no need to declare or import anything else. +The default test module is pre-configured with +something like the `BrowserModule` from `@angular/platform-browser`. + +Later you'll call `TestBed.configureTestingModule()` with +imports, providers, and more declarations to suit your testing needs. +Optional `override` methods can further fine-tune aspects of the configuration. + +
+ +{@a create-component} + +### _createComponent()_ + +After configuring `TestBed`, you call its `createComponent()` method. + + + + +`TestBed.createComponent()` creates an instance of the `BannerComponent`, +adds a corresponding element to the test-runner DOM, +and returns a [`ComponentFixture`](#component-fixture). + +
+ +Do not re-configure `TestBed` after calling `createComponent`. + +The `createComponent` method freezes the current `TestBed` definition, +closing it to further configuration. + +You cannot call any more `TestBed` configuration methods, not `configureTestingModule()`, +nor `get()`, nor any of the `override...` methods. +If you try, `TestBed` throws an error. + +
+ +{@a component-fixture} + +### _ComponentFixture_ + +The [ComponentFixture](api/core/testing/ComponentFixture) is a test harness for interacting with the created component and its corresponding element. + +Access the component instance through the fixture and confirm it exists with a Jasmine expectation: + + + + +### _beforeEach()_ + +You will add more tests as this component evolves. +Rather than duplicate the `TestBed` configuration for each test, +you refactor to pull the setup into a Jasmine `beforeEach()` and some supporting variables: + + + +Now add a test that gets the component's element from `fixture.nativeElement` and +looks for the expected text. + + + + +{@a native-element} + +### _nativeElement_ + +The value of `ComponentFixture.nativeElement` has the `any` type. +Later you'll encounter the `DebugElement.nativeElement` and it too has the `any` type. + +Angular can't know at compile time what kind of HTML element the `nativeElement` is or +if it even is an HTML element. +The app might be running on a _non-browser platform_, such as the server or a +[Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API), +where the element may have a diminished API or not exist at all. + +The tests in this guide are designed to run in a browser so a +`nativeElement` value will always be an `HTMLElement` or +one of its derived classes. + +Knowing that it is an `HTMLElement` of some sort, you can use +the standard HTML `querySelector` to dive deeper into the element tree. + +Here's another test that calls `HTMLElement.querySelector` to get the paragraph element and look for the banner text: + + + + +{@a debug-element} + +### _DebugElement_ + +The Angular _fixture_ provides the component's element directly through the `fixture.nativeElement`. + + + + +This is actually a convenience method, implemented as `fixture.debugElement.nativeElement`. + + + + +There's a good reason for this circuitous path to the element. + +The properties of the `nativeElement` depend upon the runtime environment. +You could be running these tests on a _non-browser_ platform that doesn't have a DOM or +whose DOM-emulation doesn't support the full `HTMLElement` API. + +Angular relies on the `DebugElement` abstraction to work safely across _all supported platforms_. +Instead of creating an HTML element tree, Angular creates a `DebugElement` tree that wraps the _native elements_ for the runtime platform. +The `nativeElement` property unwraps the `DebugElement` and returns the platform-specific element object. + +Because the sample tests for this guide are designed to run only in a browser, +a `nativeElement` in these tests is always an `HTMLElement` +whose familiar methods and properties you can explore within a test. + +Here's the previous test, re-implemented with `fixture.debugElement.nativeElement`: + + + + +The `DebugElement` has other methods and properties that +are useful in tests, as you'll see elsewhere in this guide. + +You import the `DebugElement` symbol from the Angular core library. + + + + +{@a by-css} +### _By.css()_ + +Although the tests in this guide all run in the browser, +some apps might run on a different platform at least some of the time. + +For example, the component might render first on the server as part of a strategy to make the application launch faster on poorly connected devices. The server-side renderer might not support the full HTML element API. +If it doesn't support `querySelector`, the previous test could fail. + +The `DebugElement` offers query methods that work for all supported platforms. +These query methods take a _predicate_ function that returns `true` when a node in the `DebugElement` tree matches the selection criteria. + +You create a _predicate_ with the help of a `By` class imported from a +library for the runtime platform. Here's the `By` import for the browser platform: + + + + +The following example re-implements the previous test with +`DebugElement.query()` and the browser's `By.css` method. + + + + +Some noteworthy observations: + +- The `By.css()` static method selects `DebugElement` nodes + with a [standard CSS selector](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_started/Selectors 'CSS selectors'). +- The query returns a `DebugElement` for the paragraph. +- You must unwrap that result to get the paragraph element. + +When you're filtering by CSS selector and only testing properties of a browser's _native element_, the `By.css` approach may be overkill. + +It's often easier and more clear to filter with a standard `HTMLElement` method +such as `querySelector()` or `querySelectorAll()`, +as you'll see in the next set of tests. + diff --git a/aio/content/guide/testing-components-scenarios.md b/aio/content/guide/testing-components-scenarios.md new file mode 100644 index 0000000000..5f94cdef45 --- /dev/null +++ b/aio/content/guide/testing-components-scenarios.md @@ -0,0 +1,1826 @@ +# Component testing scenarios + +This guide explores common component testing use cases. + +
+ + For the sample app that the testing guides describe, see the sample app. + + For the tests features in the testing guides, see tests. + +
+ +## Component binding + +In the example app, the `BannerComponent` presents static title text in the HTML template. + +After a few changes, the `BannerComponent` presents a dynamic title by binding to +the component's `title` property like this. + + + +As minimal as this is, you decide to add a test to confirm that component +actually displays the right content where you think it should. + +#### Query for the _<h1>_ + +You'll write a sequence of tests that inspect the value of the `

` element +that wraps the _title_ property interpolation binding. + +You update the `beforeEach` to find that element with a standard HTML `querySelector` +and assign it to the `h1` variable. + + + +{@a detect-changes} + +#### _createComponent()_ does not bind data + +For your first test you'd like to see that the screen displays the default `title`. +Your instinct is to write a test that immediately inspects the `

` like this: + + + + +_That test fails_ with the message: + +```javascript +expected '' to contain 'Test Tour of Heroes'. +``` + +Binding happens when Angular performs **change detection**. + +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; a fact confirmed in the revised test: + + + +#### _detectChanges()_ + +You must tell the `TestBed` to perform data binding by calling `fixture.detectChanges()`. +Only then does the `

` have the expected title. + + + + +Delayed change detection is intentional and useful. +It gives the tester an opportunity to inspect and change the state of +the component _before Angular initiates data binding and calls [lifecycle hooks](guide/lifecycle-hooks)_. + +Here's another test that changes the component's `title` property _before_ calling `fixture.detectChanges()`. + + + + +{@a auto-detect-changes} + +#### Automatic change detection + +The `BannerComponent` tests frequently call `detectChanges`. +Some testers prefer that the Angular test environment run change detection automatically. + +That's possible by configuring the `TestBed` with the `ComponentFixtureAutoDetect` provider. +First import it from the testing utility library: + + + +Then add it to the `providers` array of the testing module configuration: + + + +Here are three tests that illustrate how automatic change detection works. + + + +The first test shows the benefit of automatic change detection. + +The second and third test reveal an important limitation. +The Angular testing environment does _not_ know that the test changed the component's `title`. +The `ComponentFixtureAutoDetect` service responds to _asynchronous activities_ such as promise resolution, timers, and DOM events. +But a direct, synchronous update of the component property is invisible. +The test must call `fixture.detectChanges()` manually to trigger another cycle of change detection. + +
+ +Rather than wonder when the test fixture will or won't perform change detection, +the samples in this guide _always call_ `detectChanges()` _explicitly_. +There is no harm in calling `detectChanges()` more often than is strictly necessary. + +
+ +
+ +{@a dispatch-event} + +#### Change an input value with _dispatchEvent()_ + +To simulate user input, you can find the input element and set its `value` property. + +You will call `fixture.detectChanges()` to trigger Angular's change detection. +But there is an essential, intermediate step. + +Angular doesn't know that you set the input element's `value` property. +It won't read that property until you raise the element's `input` event by calling `dispatchEvent()`. +_Then_ you call `detectChanges()`. + +The following example demonstrates the proper sequence. + + + +
+ +## Component with external files + +The `BannerComponent` above is defined with an _inline template_ and _inline css_, specified in the `@Component.template` and `@Component.styles` properties respectively. + +Many components specify _external templates_ and _external css_ with the +`@Component.templateUrl` and `@Component.styleUrls` properties respectively, +as the following variant of `BannerComponent` does. + + + +This syntax tells the Angular compiler to read the external files during component compilation. + +That's not a problem when you run the CLI `ng test` command because it +_compiles the app before running the tests_. + +However, if you run the tests in a **non-CLI environment**, +tests of this component may fail. +For example, if you run the `BannerComponent` tests in a web coding environment such as [plunker](https://plnkr.co/), you'll see a message like this one: + + +Error: This test module uses the component BannerComponent +which is using a "templateUrl" or "styleUrls", but they were never compiled. +Please call "TestBed.compileComponents" before your test. + + +You get this test failure message when the runtime environment +compiles the source code _during the tests themselves_. + +To correct the problem, call `compileComponents()` as explained [below](#compile-components). + +{@a component-with-dependency} + +## 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`: + + + +The `WelcomeComponent` has decision logic that interacts with the service, logic that makes this component worth testing. +Here's the testing module configuration for the spec file, `app/welcome/welcome.component.spec.ts`: + + + +This time, in addition to declaring the _component-under-test_, +the configuration adds a `UserService` provider to the `providers` list. +But not the real `UserService`. + +{@a service-test-doubles} + +#### 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 ask the user for login credentials and +attempt to reach an authentication server. +These behaviors can be hard to intercept. +It is far easier and safer to create and register a test double in place of the real `UserService`. + +This particular test suite supplies a minimal mock of the `UserService` that satisfies the needs of the `WelcomeComponent` and its tests: + + + +{@a get-injected-service} + +#### Get injected services + +The tests need access to the (stub) `UserService` injected into the `WelcomeComponent`. + +Angular has a hierarchical injection system. +There can be injectors at multiple levels, from the root injector created by the `TestBed` +down through the component tree. + +The safest way to get the injected service, the way that **_always works_**, +is to **get it from the injector of the _component-under-test_**. +The component injector is a property of the fixture's `DebugElement`. + + + + +{@a testbed-inject} + +#### _TestBed.inject()_ + +You _may_ also be able to get the service from the root injector via `TestBed.inject()`. +This is easier to remember and less verbose. +But it only works when Angular injects the component with the service instance in the test's root injector. + +In this test suite, the _only_ provider of `UserService` is the root testing module, +so it is safe to call `TestBed.inject()` as follows: + + + + +
+ +For a use case in which `TestBed.inject()` does not work, +see the [_Override component providers_](#component-override) section that +explains when and why you must get the service from the component's injector instead. + +
+ +{@a welcome-spec-setup} + +#### Final setup and tests + +Here's the complete `beforeEach()`, using `TestBed.inject()`: + + + +And here are some tests: + + + +The first is a sanity test; it confirms that the stubbed `UserService` is called and working. + +
+ +The second parameter to the Jasmine matcher (e.g., `'expected name'`) is an optional failure label. +If the expectation fails, Jasmine appends this label to the expectation failure message. +In a spec with multiple expectations, it can help clarify what went wrong and which expectation failed. + +
+ +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 component-with-async-service} + +## Component with async service + +In this sample, the `AboutComponent` template hosts a `TwainComponent`. +The `TwainComponent` displays Mark Twain quotes. + + + +Note that the value of the component's `quote` property passes through an `AsyncPipe`. +That means the property returns either a `Promise` or an `Observable`. + +In this example, the `TwainComponent.getQuote()` method tells you that +the `quote` property returns an `Observable`. + + + +The `TwainComponent` gets quotes from an injected `TwainService`. +The component starts the returned `Observable` with a placeholder value (`'...'`), +before the service can return its first quote. + +The `catchError` intercepts service errors, prepares an error message, +and returns the placeholder value on the success channel. +It must wait a tick to set the `errorMessage` +in order to avoid updating that message twice in the same change detection cycle. + +These are all features you'll want to test. + +#### Testing with a spy + +When testing a component, only the service's public API should matter. +In general, tests themselves should not make calls to remote servers. +They should emulate such calls. The setup in this `app/twain/twain.component.spec.ts` shows one way to do that: + + + +{@a service-spy} + +Focus on the spy. + + + + +The spy is designed such that any call to `getQuote` receives an observable with a test quote. +Unlike the real `getQuote()` method, this spy bypasses the server +and returns a synchronous observable whose value is available immediately. + +You can write many useful tests with this spy, even though its `Observable` is synchronous. + +{@a sync-tests} + +#### Synchronous tests + +A key advantage of a synchronous `Observable` is that +you can often turn asynchronous processes into synchronous tests. + + + + +Because the spy result returns synchronously, the `getQuote()` method updates +the message on screen immediately _after_ +the first change detection cycle during which Angular calls `ngOnInit`. + +You're not so lucky when testing the error path. +Although the service spy will return an error synchronously, +the component method calls `setTimeout()`. +The test must wait at least one full turn of the JavaScript engine before the +value becomes available. The test must become _asynchronous_. + +{@a fake-async} + +#### Async test with _fakeAsync()_ + +To use `fakeAsync()` functionality, you must import `zone.js/dist/zone-testing` in your test setup file. +If you created your project with the Angular CLI, `zone-testing` is already imported in `src/test.ts`. + +The following test confirms the expected behavior when the service returns an `ErrorObservable`. + + + + +Note that the `it()` function receives an argument of the following form. + +```javascript +fakeAsync(() => { /* test body */ }) +``` + +The `fakeAsync()` function enables a linear coding style by running the test body in a special `fakeAsync test zone`. +The test body appears to be synchronous. +There is no nested syntax (like a `Promise.then()`) to disrupt the flow of control. + +
+ +Limitation: The `fakeAsync()` function won't work if the test body makes an `XMLHttpRequest` (XHR) call. +XHR calls within a test are rare, but if you need to call XHR, see [`async()`](#async), below. + +
+ +{@a tick} + +#### The _tick()_ function + +You do have to call [tick()](api/core/testing/tick) to advance the (virtual) clock. + +Calling [tick()](api/core/testing/tick) simulates the passage of time until all pending asynchronous activities finish. +In this case, it waits for the error handler's `setTimeout()`. + +The [tick()](api/core/testing/tick) function accepts milliseconds and tickOptions as parameters, the millisecond (defaults to 0 if not provided) parameter represents how much the virtual clock advances. For example, if you have a `setTimeout(fn, 100)` in a `fakeAsync()` test, you need to use tick(100) to trigger the fn callback. The tickOptions is an optional parameter with a property called `processNewMacroTasksSynchronously` (defaults to true) represents whether to invoke new generated macro tasks when ticking. + + + + +The [tick()](api/core/testing/tick) function is one of the Angular testing utilities that you import with `TestBed`. +It's a companion to `fakeAsync()` and you can only call it within a `fakeAsync()` body. + +#### tickOptions + + + + +In this example, we have a new macro task (nested setTimeout), by default, when we `tick`, the setTimeout `outside` and `nested` will both be triggered. + + + + +And in some case, we don't want to trigger the new macro task when ticking, we can use `tick(milliseconds, {processNewMacroTasksSynchronously: false})` to not invoke new macro task. + +#### Comparing dates inside fakeAsync() + +`fakeAsync()` simulates passage of time, which allows you to calculate the difference between dates inside `fakeAsync()`. + + + + +#### jasmine.clock with fakeAsync() + +Jasmine also provides a `clock` feature to mock dates. Angular automatically runs tests that are run after +`jasmine.clock().install()` is called inside a `fakeAsync()` method until `jasmine.clock().uninstall()` is called. `fakeAsync()` is not needed and throws an error if nested. + +By default, this feature is disabled. To enable it, set a global flag before importing `zone-testing`. + +If you use the Angular CLI, configure this flag in `src/test.ts`. + +``` +(window as any)['__zone_symbol__fakeAsyncPatchLock'] = true; +import 'zone.js/dist/zone-testing'; +``` + + + + +#### Using the RxJS scheduler inside fakeAsync() + +You can also use RxJS scheduler in `fakeAsync()` just like using `setTimeout()` or `setInterval()`, but you need to import `zone.js/dist/zone-patch-rxjs-fake-async` to patch RxJS scheduler. + + + +#### Support more macroTasks + +By default, `fakeAsync()` supports the following macro tasks. + +- `setTimeout` +- `setInterval` +- `requestAnimationFrame` +- `webkitRequestAnimationFrame` +- `mozRequestAnimationFrame` + +If you run other macro tasks such as `HTMLCanvasElement.toBlob()`, an _"Unknown macroTask scheduled in fake async test"_ error will be thrown. + + + + + + + + +If you want to support such a case, you need to define the macro task you want to support in `beforeEach()`. +For example: + + + + +Note that in order to make the `` element Zone.js-aware in your app, you need to import the `zone-patch-canvas` patch (either in `polyfills.ts` or in the specific file that uses ``): + + + + + +#### Async observables + +You might be satisfied with the test coverage of these tests. + +However, you might be troubled by the fact that the real service doesn't quite behave this way. +The real service sends requests to a remote server. +A server takes time to respond and the response certainly won't be available immediately +as in the previous two tests. + +Your tests will reflect the real world more faithfully if you return an _asynchronous_ observable +from the `getQuote()` spy like this. + + + + +#### Async observable helpers + +The async observable was produced by an `asyncData` helper. +The `asyncData` helper is a utility function that you'll have to write yourself, or you can copy this one from the sample code. + + + + +This helper's observable emits the `data` value in the next turn of the JavaScript engine. + +The [RxJS `defer()` operator](http://reactivex.io/documentation/operators/defer.html) returns an observable. +It takes a factory function that returns either a promise or an observable. +When something subscribes to _defer_'s observable, +it adds the subscriber to a new observable created with that factory. + +The `defer()` operator transforms the `Promise.resolve()` into a new observable that, +like `HttpClient`, emits once and completes. +Subscribers are unsubscribed after they receive the data value. + +There's a similar helper for producing an async error. + + + + +#### More async tests + +Now that the `getQuote()` spy is returning async observables, +most of your tests will have to be async as well. + +Here's a `fakeAsync()` test that demonstrates the data flow you'd expect +in the real world. + + + + +Notice that the quote element displays the placeholder value (`'...'`) after `ngOnInit()`. +The first quote hasn't arrived yet. + +To flush the first quote from the observable, you call [tick()](api/core/testing/tick). +Then call `detectChanges()` to tell Angular to update the screen. + +Then you can assert that the quote element displays the expected text. + +{@a async} + +#### Async test with _async()_ + +To use `async()` functionality, you must import `zone.js/dist/zone-testing` in your test setup file. +If you created your project with the Angular CLI, `zone-testing` is already imported in `src/test.ts`. + +The `fakeAsync()` utility function has a few limitations. +In particular, it won't work if the test body makes an `XMLHttpRequest` (XHR) call. +XHR calls within a test are rare so you can generally stick with [`fakeAsync()`](#fake-async). +But if you ever do need to call `XMLHttpRequest`, you'll want to know about `async()`. + +
+ +The `TestBed.compileComponents()` method (see [below](#compile-components)) calls `XHR` +to read external template and css files during "just-in-time" compilation. +Write tests that call `compileComponents()` with the `async()` utility. + +
+ +Here's the previous `fakeAsync()` test, re-written with the `async()` utility. + + + + +The `async()` utility hides some asynchronous boilerplate by arranging for the tester's code +to run in a special _async test zone_. +You don't need to pass Jasmine's `done()` into the test and call `done()` because it is `undefined` in promise or observable callbacks. + +But the test's asynchronous nature is revealed by the call to `fixture.whenStable()`, +which breaks the linear flow of control. + +When using an `intervalTimer()` such as `setInterval()` in `async()`, remember to cancel the timer with `clearInterval()` after the test, otherwise the `async()` never ends. + +{@a when-stable} + +#### _whenStable_ + +The test must wait for the `getQuote()` observable to emit the next quote. +Instead of calling [tick()](api/core/testing/tick), it calls `fixture.whenStable()`. + +The `fixture.whenStable()` returns a promise that resolves when the JavaScript engine's +task queue becomes empty. +In this example, the task queue becomes empty when the observable emits the first quote. + +The test resumes within the promise callback, which calls `detectChanges()` to +update the quote element with the expected text. + +{@a jasmine-done} + +#### Jasmine _done()_ + +While the `async()` and `fakeAsync()` functions greatly +simplify Angular asynchronous testing, +you can still fall back to the traditional technique +and pass `it` a function that takes a +[`done` callback](https://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support). + +You can't call `done()` in `async()` or `fakeAsync()` functions, because the `done parameter` +is `undefined`. + +Now you are responsible for chaining promises, handling errors, and calling `done()` at the appropriate moments. + +Writing test functions with `done()`, is more cumbersome than `async()`and `fakeAsync()`, but it is occasionally necessary when code involves the `intervalTimer()` like `setInterval`. + +Here are two more versions of the previous test, written with `done()`. +The first one subscribes to the `Observable` exposed to the template by the component's `quote` property. + + + +The RxJS `last()` operator emits the observable's last value before completing, which will be the test quote. +The `subscribe` callback calls `detectChanges()` to +update the quote element with the test quote, in the same manner as the earlier tests. + +In some tests, you're more interested in how an injected service method was called and what values it returned, +than what appears on screen. + +A service spy, such as the `qetQuote()` spy of the fake `TwainService`, +can give you that information and make assertions about the state of the view. + + + +
+ +{@a marble-testing} +## Component marble tests + +The previous `TwainComponent` tests simulated an asynchronous observable response +from the `TwainService` with the `asyncData` and `asyncError` utilities. + +These are short, simple functions that you can write yourself. +Unfortunately, they're too simple for many common scenarios. +An observable often emits multiple times, perhaps after a significant delay. +A component may coordinate multiple observables +with overlapping sequences of values and errors. + +**RxJS marble testing** is a great way to test observable scenarios, +both simple and complex. +You've likely seen the [marble diagrams](http://rxmarbles.com/) +that illustrate how observables work. +Marble testing uses a similar marble language to +specify the observable streams and expectations in your tests. + +The following examples revisit two of the `TwainComponent` tests +with marble testing. + +Start by installing the `jasmine-marbles` npm package. +Then import the symbols you need. + + + +Here's the complete test for getting a quote: + + + +Notice that the Jasmine test is synchronous. There's no `fakeAsync()`. +Marble testing uses a test scheduler to simulate the passage of time +in a synchronous test. + +The beauty of marble testing is in the visual definition of the observable streams. +This test defines a [_cold_ observable](#cold-observable) that waits +three [frames](#marble-frame) (`---`), +emits a value (`x`), and completes (`|`). +In the second argument you map the value marker (`x`) to the emitted value (`testQuote`). + + + +The marble library constructs the corresponding observable, which the +test sets as the `getQuote` spy's return value. + +When you're ready to activate the marble observables, +you tell the `TestScheduler` to _flush_ its queue of prepared tasks like this. + + + +This step serves a purpose analogous to [tick()](api/core/testing/tick) and `whenStable()` in the +earlier `fakeAsync()` and `async()` examples. +The balance of the test is the same as those examples. + +#### Marble error testing + +Here's the marble testing version of the `getQuote()` error test. + + + +It's still an async test, calling `fakeAsync()` and [tick()](api/core/testing/tick), because the component itself +calls `setTimeout()` when processing errors. + +Look at the marble observable definition. + + + +This is a _cold_ observable that waits three frames and then emits an error, +The hash (`#`) indicates the timing of the error that is specified in the third argument. +The second argument is null because the observable never emits a value. + +#### Learn about marble testing + +{@a marble-frame} +A _marble frame_ is a virtual unit of testing time. +Each symbol (`-`, `x`, `|`, `#`) marks the passing of one frame. + +{@a cold-observable} +A _cold_ observable doesn't produce values until you subscribe to it. +Most of your application observables are cold. +All [_HttpClient_](guide/http) methods return cold observables. + +A _hot_ observable is already producing values _before_ you subscribe to it. +The [_Router.events_](api/router/Router#events) observable, +which reports router activity, is a _hot_ observable. + +RxJS marble testing is a rich subject, beyond the scope of this guide. +Learn about it on the web, starting with the +[official documentation](https://github.com/ReactiveX/rxjs/blob/master/doc/writing-marble-tests.md). + +
+ +{@a component-with-input-output} + +## 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 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 a tiny example of a component in this role. +It displays an individual hero provided by the `DashboardComponent`. +Clicking that hero tells the `DashboardComponent` that the user has selected the hero. + +The `DashboardHeroComponent` is embedded in the `DashboardComponent` template like this: + + + +The `DashboardHeroComponent` appears in an `*ngFor` repeater, which sets each component's `hero` input property +to the looping value and listens for the component's `selected` event. + +Here's the component's full definition: + +{@a dashboard-hero-component} + + + +While testing a component this simple has little intrinsic value, it's worth knowing how. +You can use one of these approaches: + +- Test it as used by `DashboardComponent`. +- Test it as a stand-alone component. +- Test it as used by a substitute for `DashboardComponent`. + +A quick look at the `DashboardComponent` constructor discourages the first approach: + + + +The `DashboardComponent` depends on the Angular router and the `HeroService`. +You'd probably have to replace them both with test doubles, which is a lot of work. +The router seems particularly challenging. + +
+ +The [discussion below](#routing-component) covers testing components that require the router. + +
+ +The immediate goal is to test the `DashboardHeroComponent`, not the `DashboardComponent`, +so, try the second and third options. + +{@a dashboard-standalone} + +#### Test _DashboardHeroComponent_ stand-alone + +Here's the meat of the spec file setup. + + + +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 following test verifies that the hero name is propagated to the template via a binding. + + + + +Because the [template](#dashboard-hero-component) passes the hero name through the Angular `UpperCasePipe`, +the test must match the element value with the upper-cased name. + +
+ +This small test demonstrates how Angular tests can verify a component's visual +representation—something not possible with +[component class tests](guide/testing-components-basics#component-class-testing)—at +low cost and without resorting to much slower and more complicated end-to-end tests. + +
+ +#### Clicking + +Clicking the hero should raise a `selected` event that +the host component (`DashboardComponent` presumably) can hear: + + + + +The component's `selected` property returns an `EventEmitter`, +which looks like an RxJS synchronous `Observable` to consumers. +The test subscribes to it _explicitly_ just as the host component does _implicitly_. + +If the component behaves as expected, clicking the hero's element +should tell the component's `selected` property to emit the `hero` object. + +The test detects that event through its subscription to `selected`. + +{@a trigger-event-handler} + +#### _triggerEventHandler_ + +The `heroDe` in the previous test is a `DebugElement` that represents the hero `
`. + +It has Angular properties and methods that abstract interaction with the native element. +This test calls the `DebugElement.triggerEventHandler` with the "click" event name. +The "click" event binding responds by calling `DashboardHeroComponent.click()`. + +The Angular `DebugElement.triggerEventHandler` can raise _any data-bound event_ by its _event name_. +The second parameter is the event object passed to the handler. + +The test triggered a "click" event with a `null` event object. + + + + +The test assumes (correctly in this case) that the runtime +event handler—the component's `click()` method—doesn't +care about the event object. + +
+ +Other handlers are less forgiving. For example, the `RouterLink` +directive expects an object with a `button` property +that identifies which mouse button (if any) was pressed during the click. +The `RouterLink` directive throws an error if the event object is missing. + +
+ +#### Click the element + +The following test alternative calls the native element's own `click()` method, +which is perfectly fine for _this component_. + + + + +{@a click-helper} + +#### _click()_ helper + +Clicking a button, an anchor, or an arbitrary HTML element is a common test task. + +Make that consistent and easy by encapsulating the _click-triggering_ process +in a helper such as the `click()` function below: + + + +The first parameter is the _element-to-click_. If you wish, you can pass a +custom event object as the second parameter. The default is a (partial) +left-button mouse event object +accepted by many handlers including the `RouterLink` directive. + +
+ +The `click()` helper function is **not** one of the Angular testing utilities. +It's a function defined in _this guide's sample code_. +All of the sample tests use it. +If you like it, add it to your own collection of helpers. + +
+ +Here's the previous test, rewritten using the click helper. + + + + +
+ +{@a component-inside-test-host} + +## Component inside a test host + +The previous tests played the role of the host `DashboardComponent` themselves. +But does the `DashboardHeroComponent` work correctly when properly data-bound to a host component? + +You could test with the actual `DashboardComponent`. +But doing so could require a lot of setup, +especially when its template features an `*ngFor` repeater, +other components, layout HTML, additional bindings, +a constructor that injects multiple services, +and it starts interacting with those services right away. + +Imagine the effort to disable these distractions, just to prove a point +that can be made satisfactorily with a _test host_ like this one: + + + +This test host binds to `DashboardHeroComponent` as the `DashboardComponent` would +but without the noise of the `Router`, the `HeroService`, or 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, +which records the emitted hero in its `selectedHero` property. + +Later, the tests will be able to easily check `selectedHero` to verify that the +`DashboardHeroComponent.selected` event emitted the expected hero. + +The setup for the _test-host_ tests is similar to the setup for the stand-alone tests: + + + +This testing module configuration shows three important differences: + +1. It _declares_ both the `DashboardHeroComponent` and the `TestHostComponent`. +1. It _creates_ the `TestHostComponent` instead of the `DashboardHeroComponent`. +1. The `TestHostComponent` sets the `DashboardHeroComponent.hero` with a binding. + +The `createComponent` returns a `fixture` that holds an instance of `TestHostComponent` instead of an instance of `DashboardHeroComponent`. + +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: + + + +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 routing-component} + +## Routing component + +A _routing component_ is a component that tells the `Router` to navigate to another component. +The `DashboardComponent` is a _routing component_ because the user can +navigate to the `HeroDetailComponent` by clicking on one of the _hero buttons_ on the dashboard. + +Routing is pretty complicated. +Testing the `DashboardComponent` seemed daunting in part because it involves the `Router`, +which it injects together with the `HeroService`. + + + +Mocking the `HeroService` with a spy is a [familiar story](#component-with-async-service). +But the `Router` has a complicated API and is entwined with other services and application preconditions. Might it be difficult to mock? + +Fortunately, not in this case because the `DashboardComponent` isn't doing much with the `Router` + + + + +This is often the case with _routing components_. +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. + +Providing a router spy for _this component_ test suite happens to be as easy +as providing a `HeroService` spy. + + + +The following test clicks the displayed hero and confirms that +`Router.navigateByUrl` is called with the expected url. + + + +{@a routed-component-w-param} + +## Routed components + +A _routed component_ is the destination of a `Router` navigation. +It can be trickier to test, especially when the route to the component _includes parameters_. +The `HeroDetailComponent` is a _routed component_ that is the destination of such a route. + +When a user clicks a _Dashboard_ hero, the `DashboardComponent` tells the `Router` +to navigate to `heroes/:id`. +The `:id` is a route parameter whose value is the `id` of the hero to edit. + +The `Router` matches that URL to a route to the `HeroDetailComponent`. +It creates an `ActivatedRoute` object with the routing information and +injects it into a new instance of the `HeroDetailComponent`. + +Here's the `HeroDetailComponent` constructor: + + + +The `HeroDetail` component needs the `id` parameter so it can fetch +the corresponding hero via the `HeroDetailService`. +The component has to get the `id` from the `ActivatedRoute.paramMap` property +which is an `Observable`. + +It can't just reference the `id` property of the `ActivatedRoute.paramMap`. +The component has to _subscribe_ to the `ActivatedRoute.paramMap` observable and be prepared +for the `id` to change during its lifetime. + + + +
+ +The [ActivatedRoute in action](guide/router#activated-route-in-action) section of the [Router](guide/router) guide covers `ActivatedRoute.paramMap` in more detail. + +
+ +Tests can explore how the `HeroDetailComponent` responds to different `id` parameter values +by manipulating the `ActivatedRoute` injected into the component's constructor. + +You know how to spy on the `Router` and a data service. + +You'll take a different approach with `ActivatedRoute` because + +- `paramMap` returns an `Observable` that can emit more than one value + during a test. +- You need the router helper function, `convertToParamMap()`, to create a `ParamMap`. +- Other _routed component_ tests need a test double for `ActivatedRoute`. + +These differences argue for a re-usable stub class. + +#### _ActivatedRouteStub_ + +The following `ActivatedRouteStub` class serves as a test double for `ActivatedRoute`. + + + +Consider placing such helpers in a `testing` folder sibling to the `app` folder. +This sample puts `ActivatedRouteStub` in `testing/activated-route-stub.ts`. + +
+ +Consider writing a more capable version of this stub class with +the [_marble testing library_](#marble-testing). + +
+ +{@a tests-w-test-double} + +#### Testing with _ActivatedRouteStub_ + +Here's a test demonstrating the component's behavior when the observed `id` refers to an existing hero: + + + +
+ +The `createComponent()` method and `page` object are discussed [below](#page-object). +Rely on your intuition for now. + +
+ +When the `id` cannot be found, the component should re-route to the `HeroListComponent`. + +The test suite setup provided the same router spy [described above](#routing-component) which spies on the router without actually navigating. + +This test expects the component to try to navigate to the `HeroListComponent`. + + + +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: + + + +
+ +## Nested component tests + +Component templates often have nested components, whose templates +may contain more components. + +The component tree can be very deep and, most of the time, the nested components +play no role in testing the component at the top of the tree. + +The `AppComponent`, for example, displays a navigation bar with anchors and their `RouterLink` directives. + + + +While the `AppComponent` _class_ is empty, +you may want to write unit tests to confirm that the links are wired properly +to the `RouterLink` directives, perhaps for the reasons [explained below](#why-stubbed-routerlink-tests). + +To validate the links, you don't need the `Router` to navigate and you don't +need the `` to mark where the `Router` inserts _routed components_. + +The `BannerComponent` and `WelcomeComponent` +(indicated by `` and ``) are also irrelevant. + +Yet any test that creates the `AppComponent` in the DOM will also create instances of +these three components and, if you let that happen, +you'll have to configure the `TestBed` to create them. + +If you neglect to declare them, the Angular compiler won't recognize the +``, ``, and `` tags in the `AppComponent` template +and will throw an error. + +If you declare the real components, you'll also have to declare _their_ nested components +and provide for _all_ services injected in _any_ component in the tree. + +That's too much effort just to answer a few simple questions about links. + +This section describes two techniques for minimizing the setup. +Use them, alone or in combination, to stay focused on the testing the primary component. + +{@a stub-component} + +##### Stubbing unneeded components + +In the first technique, you create and declare stub versions of the components +and directive that play little or no role in the tests. + + + +The stub selectors match the selectors for the corresponding real components. +But their templates and classes are empty. + +Then declare them in the `TestBed` configuration next to the +components, directives, and pipes that need to be real. + + + +The `AppComponent` is the test subject, so of course you declare the real version. + +The `RouterLinkDirectiveStub`, [described later](#routerlink), is a test version +of the real `RouterLink` that helps with the link tests. + +The rest are stubs. + +{@a no-errors-schema} + +#### _NO_ERRORS_SCHEMA_ + +In the second approach, add `NO_ERRORS_SCHEMA` to the `TestBed.schemas` metadata. + + + +The `NO_ERRORS_SCHEMA` tells the Angular compiler to ignore unrecognized elements and attributes. + +The compiler will recognize the `` element and the `routerLink` attribute +because you declared a corresponding `AppComponent` and `RouterLinkDirectiveStub` +in the `TestBed` configuration. + +But the compiler won't throw an error when it encounters ``, ``, or ``. +It simply renders them as empty tags and the browser ignores them. + +You no longer need the stub components. + +#### Use both techniques together + +These are techniques for _Shallow Component Testing_ , +so-named because they reduce the visual surface of the component to just those elements +in the component's template that matter for tests. + +The `NO_ERRORS_SCHEMA` approach is the easier of the two but don't overuse it. + +The `NO_ERRORS_SCHEMA` also prevents the compiler from telling you about the missing +components and attributes that you omitted inadvertently or misspelled. +You could waste hours chasing phantom bugs that the compiler would have caught in an instant. + +The _stub component_ approach has another advantage. +While the stubs in _this_ example were empty, +you could give them stripped-down templates and classes if your tests +need to interact with them in some way. + +In practice you will combine the two techniques in the same setup, +as seen in this example. + + + +The Angular compiler creates the `BannerComponentStub` for the `` element +and applies the `RouterLinkStubDirective` to the anchors with the `routerLink` attribute, +but it ignores the `` and `` tags. + +
+ +{@a routerlink} +## Components with _RouterLink_ + +The real `RouterLinkDirective` is quite complicated and entangled with other components +and directives of the `RouterModule`. +It requires challenging setup to mock and use in tests. + +The `RouterLinkDirectiveStub` in this sample code replaces the real directive +with an alternative version designed to validate the kind of anchor tag wiring +seen in the `AppComponent` template. + + + +The URL bound to the `[routerLink]` attribute flows in to the directive's `linkParams` property. + +The `HostListener` wires the click event of the host element +(the `` anchor elements in `AppComponent`) to the stub directive's `onClick` method. + +Clicking the anchor should trigger the `onClick()` method, +which sets the stub's telltale `navigatedTo` property. +Tests inspect `navigatedTo` to confirm that clicking the anchor +set the expected route definition. + +
+ +Whether the router is configured properly to navigate with that route definition is a +question for a separate set of tests. + +
+ +{@a by-directive} +{@a inject-directive} + +#### _By.directive_ and injected directives + +A little more setup triggers the initial data binding and gets references to the navigation links: + + + +Three points of special interest: + +1. You can locate the anchor elements with an attached directive using `By.directive`. + +1. The query returns `DebugElement` wrappers around the matching elements. + +1. Each `DebugElement` exposes a dependency injector with the + specific instance of the directive attached to that element. + +The `AppComponent` links to validate are as follows: + + + +{@a app-component-tests} + +Here are some tests that confirm those links are wired to the `routerLink` directives +as expected: + + + +
+ +The "click" test _in this example_ is misleading. +It tests the `RouterLinkDirectiveStub` rather than the _component_. +This is a common failing of directive stubs. + +It has a legitimate purpose in this guide. +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. + +
+ +{@a why-stubbed-routerlink-tests} + +#### 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 guide update will explain how to write such +tests with the `RouterTestingModule`. + +
+ +
+ +{@a page-object} + +## Use a _page_ object + +The `HeroDetailComponent` is a simple view with a title, two hero fields, and two buttons. + + + +But there's plenty of template complexity even in this simple form. + + + +Tests that exercise the component need ... + +- to wait until a hero arrives before elements appear in the DOM. +- a reference to the title text. +- a reference to the name input box to inspect and set it. +- references to the two buttons so they can click them. +- spies for some of the component and router methods. + +Even a small form such as this one can produce a mess of tortured conditional setup and CSS element selection. + +Tame the complexity with a `Page` class that handles access to component properties +and encapsulates the logic that sets them. + +Here is such a `Page` class for the `hero-detail.component.spec.ts` + + + +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` object and fills in the blanks once the `hero` arrives. + + + +The [_HeroDetailComponent_ tests](#tests-w-test-double) in an earlier 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 reinforce the point. + + + +
+ +{@a compile-components} +## Calling _compileComponents()_ + +
+ +You can ignore this section if you _only_ run tests with the CLI `ng test` command +because the CLI compiles the application before running the tests. + +
+ +If you run tests in a **non-CLI environment**, the tests may fail with a message like this one: + + +Error: This test module uses the component BannerComponent +which is using a "templateUrl" or "styleUrls", but they were never compiled. +Please call "TestBed.compileComponents" before your test. + + +The root of the problem is at least one of the components involved in the test +specifies an external template or CSS file as +the following version of the `BannerComponent` does. + + + +The test fails when the `TestBed` tries to create the component. + + + +Recall that the app hasn't been compiled. +So when you call `createComponent()`, the `TestBed` compiles implicitly. + +That's not a problem when the source code is in memory. +But the `BannerComponent` requires external files +that the compiler must read from the file system, +an inherently _asynchronous_ operation. + +If the `TestBed` were allowed to continue, the tests would run and fail mysteriously +before the compiler could finished. + +The preemptive error message tells you to compile explicitly with `compileComponents()`. + +#### _compileComponents()_ is async + +You must call `compileComponents()` within an asynchronous test function. + +
+ +If you neglect to make the test function async +(e.g., forget to use `async()` as described below), +you'll see this error message + + +Error: ViewDestroyedError: Attempt to use a destroyed view + + +
+ +A typical approach is to divide the setup logic into two separate `beforeEach()` functions: + +1. An async `beforeEach()` that compiles the components +1. A synchronous `beforeEach()` that performs the remaining setup. + +To follow this pattern, import the `async()` helper with the other testing symbols. + + + + +#### The async _beforeEach_ + +Write the first async `beforeEach` like this. + + + +The `async()` helper function takes a parameterless function with the body of the setup. + +The `TestBed.configureTestingModule()` method returns the `TestBed` class so you can chain +calls to other `TestBed` static methods such as `compileComponents()`. + +In this example, the `BannerComponent` is the only component to compile. +Other examples configure the testing module with multiple components +and may import application modules that hold yet more components. +Any of them could be require external files. + +The `TestBed.compileComponents` method asynchronously compiles all components configured in the testing module. + +
+ +Do not re-configure the `TestBed` after calling `compileComponents()`. + +
+ +Calling `compileComponents()` closes the current `TestBed` instance to further configuration. +You cannot call any more `TestBed` configuration methods, not `configureTestingModule()` +nor any of the `override...` methods. The `TestBed` throws an error if you try. + +Make `compileComponents()` the last step +before calling `TestBed.createComponent()`. + +#### The synchronous _beforeEach_ + +The second, synchronous `beforeEach()` contains the remaining setup steps, +which include creating the component and querying for elements to inspect. + + + +You can count on the test runner to wait for the first asynchronous `beforeEach` to finish before calling the second. + +#### Consolidated setup + +You can consolidate the two `beforeEach()` functions into a single, async `beforeEach()`. + +The `compileComponents()` method returns a promise so you can perform the +synchronous setup tasks _after_ compilation by moving the synchronous code +into a `then(...)` callback. + + + +#### _compileComponents()_ is harmless + +There's no harm in calling `compileComponents()` when it's not required. + +The component test file generated by the CLI calls `compileComponents()` +even though it is never required when running `ng test`. + +The tests in this guide only call `compileComponents` when necessary. + +
+ +{@a import-module} + +## Setup with module imports + +Earlier component tests configured the testing module with a few `declarations` like this: + + + + +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` to 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: + + + +
+ +Notice that the `beforeEach()` is asynchronous and calls `TestBed.compileComponents` +because the `HeroDetailComponent` has an external template and css file. + +As explained in [_Calling compileComponents()_](#compile-components) above, +these tests could be run in a non-CLI environment +where Angular would have to compile them in the browser. + +
+ +#### Import a shared module + +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: + + + +It's a bit tighter and smaller, with fewer import statements (not shown). + +{@a feature-module-import} + +#### Import a feature module + +The `HeroDetailComponent` is part of the `HeroModule` [Feature Module](guide/feature-modules) that aggregates more of the interdependent pieces +including the `SharedModule`. +Try a test configuration that imports the `HeroModule` like this one: + + + +That's _really_ crisp. Only the _test doubles_ in the `providers` remain. Even the `HeroDetailComponent` declaration is gone. + +In fact, if you try to declare it, Angular will throw an error because +`HeroDetailComponent` is declared in both the `HeroModule` and the `DynamicTestModule` +created by the `TestBed`. + +
+ +Importing the component's feature module can be the easiest way to configure tests +when there are many mutual dependencies within the module and +the module is small, as feature modules tend to be. + +
+ +
+ +{@a component-override} + +## Override component providers + +The `HeroDetailComponent` provides its own `HeroDetailService`. + + + +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! + +
+ +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`. + + + +The [previous test configuration](#feature-module-import) replaces the real `HeroService` with a `TestHeroService` +that intercepts server requests and fakes their responses. + +
+ +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: + + + +Notice that `TestBed.configureTestingModule` no longer provides a (fake) `HeroService` because it's [not needed](#spy-stub). + +{@a override-component-method} + +#### The _overrideComponent_ method + +Focus on the `overrideComponent` method. + + + +It takes two arguments: the component type to override (`HeroDetailComponent`) and an override metadata object. +The [override metadata object](guide/testing-utility-apis#metadata-override-object) is a generic defined as follows: + + + type MetadataOverride<T> = { + add?: Partial<T>; + remove?: Partial<T>; + set?: Partial<T>; + }; + + +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: + + + selector?: string; + template?: string; + templateUrl?: string; + providers?: any[]; + ... + + +{@a spy-stub} + +#### Provide a _spy stub_ (_HeroDetailServiceSpy_) + +This example completely replaces the component's `providers` array with a new array containing a `HeroDetailServiceSpy`. + +The `HeroDetailServiceSpy` is a stubbed version of the real `HeroDetailService` +that fakes all necessary features of that service. +It neither injects nor delegates to the lower level `HeroService` +so there's no need to provide a test double for that. + +The related `HeroDetailComponent` tests will assert that methods of the `HeroDetailService` +were called by spying on the service methods. +Accordingly, the stub implements its methods as spies: + + + +{@a override-tests} + +#### The override tests + +Now the tests can control the component's hero directly by manipulating the spy-stub's `testHero` +and confirm that service methods were called. + + + +{@a more-overrides} + +#### 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. + +
diff --git a/aio/content/guide/testing-pipes.md b/aio/content/guide/testing-pipes.md new file mode 100644 index 0000000000..90ca07988a --- /dev/null +++ b/aio/content/guide/testing-pipes.md @@ -0,0 +1,41 @@ +# Testing Pipes + +You can test [pipes](guide/pipes) without the Angular testing utilities. + +
+ + For the sample app that the testing guides describe, see the sample app. + + For the tests features in the testing guides, see tests. + +
+ +## Testing the `TitleCasePipe` + +A pipe class has one method, `transform`, that manipulates the input +value into a transformed output value. +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 an implementation with a regular expression. + + + +Anything that uses a regular expression is worth testing thoroughly. +Use simple Jasmine to explore the expected cases and the edge cases. + + + +{@a write-tests} + +## Writing DOM tests to support a pipe test + +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 component tests such as this one: + + + diff --git a/aio/content/guide/testing-services.md b/aio/content/guide/testing-services.md new file mode 100644 index 0000000000..797cd8e7e3 --- /dev/null +++ b/aio/content/guide/testing-services.md @@ -0,0 +1,199 @@ +# Testing services + + +To check that your services are working as you intend, you can write tests specifically for them. + +
+ + For the sample app that the testing guides describe, see the sample app. + + For the tests features in the testing guides, see tests. + +
+ + +Services are often the easiest files to unit test. +Here are some synchronous and asynchronous unit tests of the `ValueService` +written without assistance from Angular testing utilities. + + + +{@a services-with-dependencies} + +## Services with dependencies + +Services often depend on other services that Angular injects into the constructor. +In many cases, it's easy to create and _inject_ these dependencies by hand while +calling the service's constructor. + +The `MasterService` is a simple example: + + + +`MasterService` delegates its only method, `getValue`, to the injected `ValueService`. + +Here are several ways to test it. + + + +The first test creates a `ValueService` with `new` and passes it to the `MasterService` constructor. + +However, injecting the real service rarely works well as most dependent services are difficult to create and control. + +Instead you can mock the dependency, use a dummy value, or create a +[spy](https://jasmine.github.io/2.0/introduction.html#section-Spies) +on the pertinent service method. + +
+ +Prefer spies as they are usually the easiest way to mock services. + +
+ +These standard testing techniques are great for unit testing services in isolation. + +However, you almost always inject services into application classes using Angular +dependency injection and you should have tests that reflect that usage pattern. +Angular testing utilities make it easy to investigate how injected services behave. + +## Testing services with the _TestBed_ + +Your app relies on Angular [dependency injection (DI)](guide/dependency-injection) +to create services. +When a service has a dependent service, DI finds or creates that dependent service. +And if that dependent service has its own dependencies, DI finds-or-creates them as well. + +As service _consumer_, you don't worry about any of this. +You don't worry about the order of constructor arguments or how they're created. + +As a service _tester_, you must at least think about the first level of service dependencies +but you _can_ let Angular DI do the service creation and deal with constructor argument order +when you use the `TestBed` testing utility to provide and create services. + +{@a testbed} + +## Angular _TestBed_ + +The `TestBed` is the most important of the Angular testing utilities. +The `TestBed` creates a dynamically-constructed Angular _test_ module that emulates +an Angular [@NgModule](guide/ngmodules). + +The `TestBed.configureTestingModule()` method takes a metadata object that can have most of the properties of an [@NgModule](guide/ngmodules). + +To test a service, you set the `providers` metadata property with an +array of the services that you'll test or mock. + + + +Then inject it inside a test by calling `TestBed.inject()` with the service class as the argument. + +
+ +**Note:** `TestBed.get()` was deprecated as of Angular version 9. +To help minimize breaking changes, Angular introduces a new function called `TestBed.inject()`, which you should use instead. +For information on the removal of `TestBed.get()`, +see its entry in the [Deprecations index](guide/deprecations#index). + +
+ + + +Or inside the `beforeEach()` if you prefer to inject the service as part of your setup. + + + +When testing a service with a dependency, provide the mock in the `providers` array. + +In the following example, the mock is a spy object. + + + +The test consumes that spy in the same way it did earlier. + + + + +{@a no-before-each} + +## Testing without _beforeEach()_ + +Most test suites in this guide call `beforeEach()` to set the preconditions for each `it()` test +and rely on the `TestBed` to create classes and inject services. + +There's another school of testing that never calls `beforeEach()` and prefers to create classes explicitly rather than use the `TestBed`. + +Here's how you might rewrite one of the `MasterService` tests in that style. + +Begin by putting re-usable, preparatory code in a _setup_ function instead of `beforeEach()`. + + + +The `setup()` function returns an object literal +with the variables, such as `masterService`, that a test might reference. +You don't define _semi-global_ variables (e.g., `let masterService: MasterService`) +in the body of the `describe()`. + +Then each test invokes `setup()` in its first line, before continuing +with steps that manipulate the test subject and assert expectations. + + + +Notice how the test uses +[_destructuring assignment_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) +to extract the setup variables that it needs. + + + + +Many developers feel this approach is cleaner and more explicit than the +traditional `beforeEach()` style. + +Although this testing guide follows the traditional style and +the default [CLI schematics](https://github.com/angular/angular-cli) +generate test files with `beforeEach()` and `TestBed`, +feel free to adopt _this alternative approach_ in your own projects. + +## Testing HTTP services + +Data services that make HTTP calls to remote servers typically inject and delegate +to the Angular [`HttpClient`](guide/http) service for XHR calls. + +You can test a data service with an injected `HttpClient` spy as you would +test any service with a dependency. + + + +
+ +The `HeroService` methods return `Observables`. You must +_subscribe_ to an observable to (a) cause it to execute and (b) +assert that the method succeeds or fails. + +The `subscribe()` method takes a success (`next`) and fail (`error`) callback. +Make sure you provide _both_ callbacks so that you capture errors. +Neglecting to do so produces an asynchronous uncaught observable error that +the test runner will likely attribute to a completely different test. + +
+ +## _HttpClientTestingModule_ + +Extended interactions between a data service and the `HttpClient` can be complex +and difficult to mock with spies. + +The `HttpClientTestingModule` can make these testing scenarios more manageable. + +While the _code sample_ accompanying this guide demonstrates `HttpClientTestingModule`, +this page defers to the [Http guide](guide/http#testing-http-requests), +which covers testing with the `HttpClientTestingModule` in detail. + diff --git a/aio/content/guide/testing-utility-apis.md b/aio/content/guide/testing-utility-apis.md new file mode 100644 index 0000000000..8152d75acf --- /dev/null +++ b/aio/content/guide/testing-utility-apis.md @@ -0,0 +1,794 @@ +# Testing Utility APIs + +This page describes the most useful Angular testing features. + +The Angular testing utilities include the `TestBed`, the `ComponentFixture`, and a handful of functions that control the test environment. +The [_TestBed_](#testbed-api-summary) and [_ComponentFixture_](#component-fixture-api-summary) classes are covered separately. + +Here's a summary of the stand-alone functions, in order of likely utility: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Function + + Description +
+ async + + + Runs the body of a test (`it`) or setup (`beforeEach`) function within a special _async test zone_. + See [discussion above](guide/testing-components-scenarios#async). + +
+ fakeAsync + + + Runs the body of a test (`it`) within a special _fakeAsync test zone_, enabling + a linear control flow coding style. See [discussion above](guide/testing-components-scenarios#fake-async). + +
+ tick + + + Simulates the passage of time and the completion of pending asynchronous activities + by flushing both _timer_ and _micro-task_ queues within the _fakeAsync test zone_. + +
+ + The curious, dedicated reader might enjoy this lengthy blog post, + ["_Tasks, microtasks, queues and schedules_"](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/). + +
+ + Accepts an optional argument that moves the virtual clock forward + by the specified number of milliseconds, + clearing asynchronous activities scheduled within that timeframe. + See [discussion above](guide/testing-components-scenarios#tick). + +
+ inject + + + Injects one or more services from the current `TestBed` injector into a test function. + It cannot inject a service provided by the component itself. + See discussion of the [debugElement.injector](guide/testing-components-scenarios#get-injected-services). + +
+ discardPeriodicTasks + + + When a `fakeAsync()` test ends with pending timer event _tasks_ (queued `setTimeOut` and `setInterval` callbacks), + the test fails with a clear error message. + + In general, a test should end with no queued tasks. + When pending timer tasks are expected, call `discardPeriodicTasks` to flush the _task_ queue + and avoid the error. + +
+ flushMicrotasks + + + When a `fakeAsync()` test ends with pending _micro-tasks_ such as unresolved promises, + the test fails with a clear error message. + + In general, a test should wait for micro-tasks to finish. + When pending microtasks are expected, call `flushMicrotasks` to flush the _micro-task_ queue + and avoid the error. + +
+ ComponentFixtureAutoDetect + + + A provider token for a service that turns on [automatic change detection](guide/testing-components-scenarios#automatic-change-detection). + +
+ getTestBed + + + Gets the current instance of the `TestBed`. + Usually unnecessary because the static class methods of the `TestBed` class are typically sufficient. + The `TestBed` instance exposes a few rarely used members that are not available as + static methods. + +
+ +
+ +{@a testbed-class-summary} + +## _TestBed_ class summary + +The `TestBed` class is one of the principal Angular testing utilities. +Its API is quite large and can be overwhelming until you've explored it, +a little at a time. Read the early part of this guide first +to get the basics before trying to absorb the full API. + +The module definition passed to `configureTestingModule` +is a subset of the `@NgModule` metadata properties. + + + type TestModuleMetadata = { + providers?: any[]; + declarations?: any[]; + imports?: any[]; + schemas?: Array<SchemaMetadata | any[]>; + }; + + +{@a metadata-override-object} + +Each override method takes a `MetadataOverride` where `T` is the kind of metadata +appropriate to the method, that is, the parameter of an `@NgModule`, +`@Component`, `@Directive`, or `@Pipe`. + + + type MetadataOverride<T> = { + add?: Partial<T>; + remove?: Partial<T>; + set?: Partial<T>; + }; + + +{@a testbed-methods} +{@a testbed-api-summary} + +The `TestBed` API consists of static class methods that either update or reference a _global_ instance of the `TestBed`. + +Internally, all static methods cover methods of the current runtime `TestBed` instance, +which is also returned by the `getTestBed()` function. + +Call `TestBed` methods _within_ a `beforeEach()` to ensure a fresh start before each individual test. + +Here are the most important static methods, in order of likely utility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Methods + + Description +
+ configureTestingModule + + + The testing shims (`karma-test-shim`, `browser-test-shim`) + establish the [initial test environment](guide/testing) and a default testing module. + The default testing module is configured with basic declaratives and some Angular service substitutes that every tester needs. + + Call `configureTestingModule` to refine the testing module configuration for a particular set of tests + by adding and removing imports, declarations (of components, directives, and pipes), and providers. + +
+ compileComponents + + + Compile the testing module asynchronously after you've finished configuring it. + You **must** call this method if _any_ of the testing module components have a `templateUrl` + or `styleUrls` because fetching component template and style files is necessarily asynchronous. + See [above](guide/testing-components-scenarios#compile-components). + + After calling `compileComponents`, the `TestBed` configuration is frozen for the duration of the current spec. + +
+ createComponent + + + Create an instance of a component of type `T` based on the current `TestBed` configuration. + After calling `compileComponent`, the `TestBed` configuration is frozen for the duration of the current spec. + +
+ overrideModule + + + Replace metadata for the given `NgModule`. Recall that modules can import other modules. + The `overrideModule` method can reach deeply into the current testing module to + modify one of these inner modules. + +
+ overrideComponent + + + Replace metadata for the given component class, which could be nested deeply + within an inner module. + +
+ overrideDirective + + + Replace metadata for the given directive class, which could be nested deeply + within an inner module. + +
+ overridePipe + + + Replace metadata for the given pipe class, which could be nested deeply + within an inner module. + +
+ {@a testbed-inject} + inject + + + Retrieve a service from the current `TestBed` injector. + + The `inject` function is often adequate for this purpose. + But `inject` throws an error if it can't provide the service. + + What if the service is optional? + + The `TestBed.inject()` method takes an optional second parameter, + the object to return if Angular can't find the provider + (`null` in this example): + + + + After calling `TestBed.inject`, the `TestBed` configuration is frozen for the duration of the current spec. + +
+ {@a testbed-initTestEnvironment} + initTestEnvironment + + + Initialize the testing environment for the entire test run. + + The testing shims (`karma-test-shim`, `browser-test-shim`) call it for you + so there is rarely a reason for you to call it yourself. + + You may call this method _exactly once_. If you must change + this default in the middle of your test run, call `resetTestEnvironment` first. + + Specify the Angular compiler factory, a `PlatformRef`, and a default Angular testing module. + Alternatives for non-browser platforms are available in the general form + `@angular/platform-/testing/`. + +
+ resetTestEnvironment + + + Reset the initial test environment, including the default testing module. + +
+ +A few of the `TestBed` instance methods are not covered by static `TestBed` _class_ methods. +These are rarely needed. + +{@a component-fixture-api-summary} + +## The _ComponentFixture_ + +The `TestBed.createComponent` +creates an instance of the component `T` +and returns a strongly typed `ComponentFixture` for that component. + +The `ComponentFixture` properties and methods provide access to the component, +its DOM representation, and aspects of its Angular environment. + +{@a component-fixture-properties} + +### _ComponentFixture_ properties + +Here are the most important properties for testers, in order of likely utility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Properties + + Description +
+ componentInstance + + + The instance of the component class created by `TestBed.createComponent`. + +
+ debugElement + + + The `DebugElement` associated with the root element of the component. + + The `debugElement` provides insight into the component and its DOM element during test and debugging. + It's a critical property for testers. The most interesting members are covered [below](#debug-element-details). + +
+ nativeElement + + + The native DOM element at the root of the component. + +
+ changeDetectorRef + + + The `ChangeDetectorRef` for the component. + + The `ChangeDetectorRef` is most valuable when testing a + component that has the `ChangeDetectionStrategy.OnPush` method + or the component's change detection is under your programmatic control. + +
+ +{@a component-fixture-methods} + +### _ComponentFixture_ methods + +The _fixture_ methods cause Angular to perform certain tasks on the component tree. +Call these method to trigger Angular behavior in response to simulated user action. + +Here are the most useful methods for testers. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Methods + + Description +
+ detectChanges + + + Trigger a change detection cycle for the component. + + Call it to initialize the component (it calls `ngOnInit`) and after your + test code, change the component's data bound property values. + Angular can't see that you've changed `personComponent.name` and won't update the `name` + binding until you call `detectChanges`. + + Runs `checkNoChanges` afterwards to confirm that there are no circular updates unless + called as `detectChanges(false)`; + +
+ autoDetectChanges + + + Set this to `true` when you want the fixture to detect changes automatically. + + When autodetect is `true`, the test fixture calls `detectChanges` immediately + after creating the component. Then it listens for pertinent zone events + and calls `detectChanges` accordingly. + When your test code modifies component property values directly, + you probably still have to call `fixture.detectChanges` to trigger data binding updates. + + The default is `false`. Testers who prefer fine control over test behavior + tend to keep it `false`. + +
+ checkNoChanges + + + Do a change detection run to make sure there are no pending changes. + Throws an exceptions if there are. +
+ isStable + + + If the fixture is currently _stable_, returns `true`. + If there are async tasks that have not completed, returns `false`. + +
+ whenStable + + + Returns a promise that resolves when the fixture is stable. + + To resume testing after completion of asynchronous activity or + asynchronous change detection, hook that promise. + See [above](guide/testing-components-scenarios#when-stable). + +
+ destroy + + + Trigger component destruction. + +
+ +{@a debug-element-details} + +#### _DebugElement_ + +The `DebugElement` provides crucial insights into the component's DOM representation. + +From the test root component's `DebugElement` returned by `fixture.debugElement`, +you can walk (and query) the fixture's entire element and component subtrees. + +Here are the most useful `DebugElement` members for testers, in approximate order of utility: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Member + + Description +
+ nativeElement + + + The corresponding DOM element in the browser (null for WebWorkers). + +
+ query + + + Calling `query(predicate: Predicate)` returns the first `DebugElement` + that matches the [predicate](#query-predicate) at any depth in the subtree. + +
+ queryAll + + + Calling `queryAll(predicate: Predicate)` returns all `DebugElements` + that matches the [predicate](#query-predicate) at any depth in subtree. + +
+ injector + + + The host dependency injector. + For example, the root element's component instance injector. + +
+ componentInstance + + + The element's own component instance, if it has one. + +
+ context + + + An object that provides parent context for this element. + Often an ancestor component instance that governs this element. + + When an element is repeated within `*ngFor`, the context is an `NgForRow` whose `$implicit` + property is the value of the row instance value. + For example, the `hero` in `*ngFor="let hero of heroes"`. + +
+ children + + + The immediate `DebugElement` children. Walk the tree by descending through `children`. + +
+ + `DebugElement` also has `childNodes`, a list of `DebugNode` objects. + `DebugElement` derives from `DebugNode` objects and there are often + more nodes than elements. Testers can usually ignore plain nodes. + +
+
+ parent + + + The `DebugElement` parent. Null if this is the root element. + +
+ name + + + The element tag name, if it is an element. + +
+ triggerEventHandler + + + Triggers the event by its name if there is a corresponding listener + in the element's `listeners` collection. + The second parameter is the _event object_ expected by the handler. + See [above](guide/testing-components-scenarios#trigger-event-handler). + + If the event lacks a listener or there's some other problem, + consider calling `nativeElement.dispatchEvent(eventObject)`. + +
+ listeners + + + The callbacks attached to the component's `@Output` properties and/or the element's event properties. + +
+ providerTokens + + + This component's injector lookup tokens. + Includes the component itself plus the tokens that the component lists in its `providers` metadata. + +
+ source + + + Where to find this element in the source component template. + +
+ references + + + Dictionary of objects associated with template local variables (e.g. `#foo`), + keyed by the local variable name. + +
+ +{@a query-predicate} + +The `DebugElement.query(predicate)` and `DebugElement.queryAll(predicate)` methods take a +predicate that filters the source element's subtree for matching `DebugElement`. + +The predicate is any method that takes a `DebugElement` and returns a _truthy_ value. +The following example finds all `DebugElements` with a reference to a template local variable named "content": + + + +The Angular `By` class has three static methods for common predicates: + +- `By.all` - return all elements. +- `By.css(selector)` - return elements with matching CSS selectors. +- `By.directive(directive)` - return elements that Angular matched to an instance of the directive class. + + + +
+ diff --git a/aio/content/guide/testing.md b/aio/content/guide/testing.md index 7a9d564dfc..557571acba 100644 --- a/aio/content/guide/testing.md +++ b/aio/content/guide/testing.md @@ -1,17 +1,35 @@ {@a top} + # Testing -This guide offers tips and techniques for unit and integration testing Angular applications. +Testing your Angular application helps you check that your app is working as you expect. -The guide presents tests of a sample application created with the [Angular CLI](cli). This sample application is much like the one created in the [_Tour of Heroes_ tutorial](tutorial). -The sample application and all tests in this guide are available for inspection and experimentation: +## Prerequisites -- Sample app -- Tests +Before writing tests for your Angular app, you should have a basic understanding of the following concepts: + +* Angular fundamentals +* JavaScript +* HTML +* CSS +* [Angular CLI](/cli)
-## Setup +The testing documentation offers tips and techniques for unit and integration testing Angular applications through a sample application created with the [Angular CLI](cli). +This sample application is much like the one in the [_Tour of Heroes_ tutorial](tutorial). + +
+ + For the sample app that the testing guides describe, see the sample app. + + For the tests features in the testing guides, see tests. + +
+ +{@a setup} + +## Set up testing The Angular CLI downloads and installs everything you need to test an Angular application with the [Jasmine test framework](https://jasmine.github.io/). @@ -39,7 +57,7 @@ Chrome ...: Executed 3 of 3 SUCCESS (0.135 secs / 0.205 secs) The last line of the log is the most important. It shows that Karma ran three tests that all passed. -A chrome browser also opens and displays the test output in the "Jasmine HTML Reporter" like this. +A Chrome browser also opens and displays the test output in the "Jasmine HTML Reporter" like this. -{@a code-coverage} -## Enable code coverage reports +
-The CLI can run unit tests and create code coverage reports. -Code coverage reports show you any parts of our code base that may not be properly tested by your unit tests. +## More info on testing -To generate a coverage report run the following command in the root of your project. +After you've set up your app for testing, you may find the following testing guides useful. - - ng test --no-watch --code-coverage - +* [Code coverage](guide/testing-code-coverage)—find out how much of your app your tests are covering and how to specify required amounts. +* [Testing services](guide/testing-services)—learn how to test the services your app uses. +* [Basics of testing components](guide/testing-components-basics)—discover the basics of testing Angular components. +* [Component testing scenarios](guide/testing-components-scenarios)—read about the various kinds of component testing scenarios and use cases. +* [Testing attribute directives](guide/testing-attribute-directives)—learn about how to test your attribute directives. +* [Testing pipes](guide/testing-pipes)—find out how to test attribute directives. +* [Debugging tests](guide/testing-attribute-directives)—uncover common testing bugs. +* [Testing utility APIs](guide/testing-utility-apis)—get familiar with Angular testing features. -When the tests are complete, the command creates a new `/coverage` folder in the project. Open the `index.html` file to see a report with your source code and code coverage values. -If you want to create code-coverage reports every time you test, you can set the following option in the CLI configuration file, `angular.json`: - -``` - "test": { - "options": { - "codeCoverage": true - } - } -``` - -### Code coverage enforcement - -The code coverage percentages let you estimate how much of your code is tested. -If your team decides on a set minimum amount to be unit tested, you can enforce this minimum with the Angular CLI. - -For example, suppose you want the code base to have a minimum of 80% code coverage. -To enable this, open the [Karma](https://karma-runner.github.io) test platform configuration file, `karma.conf.js`, and add the following in the `coverageIstanbulReporter:` key. - -``` -coverageIstanbulReporter: { - reports: [ 'html', 'lcovonly' ], - fixWebpackSourcePaths: true, - thresholds: { - statements: 80, - lines: 80, - branches: 80, - functions: 80 - } -} -``` - -The `thresholds` property causes the tool to enforce a minimum of 80% code coverage when the unit tests are run in the project. - -## Service Tests - -Services are often the easiest files to unit test. -Here are some synchronous and asynchronous unit tests of the `ValueService` -written without assistance from Angular testing utilities. - - - -{@a services-with-dependencies} - -#### Services with dependencies - -Services often depend on other services that Angular injects into the constructor. -In many cases, it's easy to create and _inject_ these dependencies by hand while -calling the service's constructor. - -The `MasterService` is a simple example: - - - -`MasterService` delegates its only method, `getValue`, to the injected `ValueService`. - -Here are several ways to test it. - - - -The first test creates a `ValueService` with `new` and passes it to the `MasterService` constructor. - -However, injecting the real service rarely works well as most dependent services are difficult to create and control. - -Instead you can mock the dependency, use a dummy value, or create a -[spy](https://jasmine.github.io/2.0/introduction.html#section-Spies) -on the pertinent service method. - -
- -Prefer spies as they are usually the easiest way to mock services. - -
- -These standard testing techniques are great for unit testing services in isolation. - -However, you almost always inject services into application classes using Angular -dependency injection and you should have tests that reflect that usage pattern. -Angular testing utilities make it easy to investigate how injected services behave. - -#### Testing services with the _TestBed_ - -Your app relies on Angular [dependency injection (DI)](guide/dependency-injection) -to create services. -When a service has a dependent service, DI finds or creates that dependent service. -And if that dependent service has its own dependencies, DI finds-or-creates them as well. - -As service _consumer_, you don't worry about any of this. -You don't worry about the order of constructor arguments or how they're created. - -As a service _tester_, you must at least think about the first level of service dependencies -but you _can_ let Angular DI do the service creation and deal with constructor argument order -when you use the `TestBed` testing utility to provide and create services. - -{@a testbed} - -#### Angular _TestBed_ - -The `TestBed` is the most important of the Angular testing utilities. -The `TestBed` creates a dynamically-constructed Angular _test_ module that emulates -an Angular [@NgModule](guide/ngmodules). - -The `TestBed.configureTestingModule()` method takes a metadata object that can have most of the properties of an [@NgModule](guide/ngmodules). - -To test a service, you set the `providers` metadata property with an -array of the services that you'll test or mock. - - - - -Then inject it inside a test by calling `TestBed.inject()` with the service class as the argument. - -
- -**Note:** `TestBed.get()` was deprecated as of Angular version 9. -To help minimize breaking changes, Angular introduces a -new function called `TestBed.inject()`, which you should use instead. -For information on the removal of `TestBed.get()`, -see its entry in the [Deprecations index](guide/deprecations#index). - -
- - - - -Or inside the `beforeEach()` if you prefer to inject the service as part of your setup. - - - - -When testing a service with a dependency, provide the mock in the `providers` array. - -In the following example, the mock is a spy object. - - - -The test consumes that spy in the same way it did earlier. - - - - -{@a no-before-each} -#### Testing without _beforeEach()_ - -Most test suites in this guide call `beforeEach()` to set the preconditions for each `it()` test -and rely on the `TestBed` to create classes and inject services. - -There's another school of testing that never calls `beforeEach()` and prefers to create classes explicitly rather than use the `TestBed`. - -Here's how you might rewrite one of the `MasterService` tests in that style. - -Begin by putting re-usable, preparatory code in a _setup_ function instead of `beforeEach()`. - - - -The `setup()` function returns an object literal -with the variables, such as `masterService`, that a test might reference. -You don't define _semi-global_ variables (e.g., `let masterService: MasterService`) -in the body of the `describe()`. - -Then each test invokes `setup()` in its first line, before continuing -with steps that manipulate the test subject and assert expectations. - - - -Notice how the test uses -[_destructuring assignment_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) -to extract the setup variables that it needs. - - - - -Many developers feel this approach is cleaner and more explicit than the -traditional `beforeEach()` style. - -Although this testing guide follows the traditional style and -the default [CLI schematics](https://github.com/angular/angular-cli) -generate test files with `beforeEach()` and `TestBed`, -feel free to adopt _this alternative approach_ in your own projects. - -#### Testing HTTP services - -Data services that make HTTP calls to remote servers typically inject and delegate -to the Angular [`HttpClient`](guide/http) service for XHR calls. - -You can test a data service with an injected `HttpClient` spy as you would -test any service with a dependency. - - - -
- -The `HeroService` methods return `Observables`. You must -_subscribe_ to an observable to (a) cause it to execute and (b) -assert that the method succeeds or fails. - -The `subscribe()` method takes a success (`next`) and fail (`error`) callback. -Make sure you provide _both_ callbacks so that you capture errors. -Neglecting to do so produces an asynchronous uncaught observable error that -the test runner will likely attribute to a completely different test. - -
- -#### _HttpClientTestingModule_ - -Extended interactions between a data service and the `HttpClient` can be complex -and difficult to mock with spies. - -The `HttpClientTestingModule` can make these testing scenarios more manageable. - -While the _code sample_ accompanying this guide demonstrates `HttpClientTestingModule`, -this page defers to the [Http guide](guide/http#testing-http-requests), -which covers testing with the `HttpClientTestingModule` in detail. - - -## Component Test Basics - -A component, unlike all other parts of an Angular application, -combines an HTML template and a TypeScript class. -The component truly is the template and the class _working together_. To adequately test a component, you should test that they work together -as intended. - -Such tests require creating the component's host element in the browser DOM, -as Angular does, and investigating the component class's interaction with -the DOM as described by its template. - -The Angular `TestBed` facilitates this kind of testing as you'll see in the sections below. -But in many cases, _testing the component class alone_, without DOM involvement, -can validate much of the component's behavior in an easier, more obvious way. - -### Component class testing - -Test a component class on its own as you would test a service class. - -Consider this `LightswitchComponent` which toggles a light on and off -(represented by an on-screen message) when the user clicks the button. - - - -You might decide only to test that the `clicked()` method -toggles the light's _on/off_ state and sets the message appropriately. - -This component class has no dependencies. To test these types of classes, follow the same steps as you would for a service that has no dependencies: - -1. Create a component using the new keyword. -2. Poke at its API. -3. Assert expectations on its public state. - - - -Here is the `DashboardHeroComponent` from the _Tour of Heroes_ tutorial. - - - -It appears within the template of a parent component, -which binds a _hero_ to the `@Input` property and -listens for an event raised through the _selected_ `@Output` property. - -You can test that the class code works without creating the `DashboardHeroComponent` -or its parent component. - - - -When a component has dependencies, you may wish to use the `TestBed` to both -create the component and its dependencies. - -The following `WelcomeComponent` depends on the `UserService` to know the name of the user to greet. - - - -You might start by creating a mock of the `UserService` that meets the minimum needs of this component. - - - -Then provide and inject _both the_ **component** _and the service_ in the `TestBed` configuration. - - - -Then exercise the component class, remembering to call the [lifecycle hook methods](guide/lifecycle-hooks) as Angular does when running the app. - - - -### Component DOM testing - -Testing the component _class_ is as easy as testing a service. - -But a component is more than just its class. -A component interacts with the DOM and with other components. -The _class-only_ tests can tell you about class behavior. -They cannot tell you if the component is going to render properly, -respond to user input and gestures, or integrate with its parent and child components. - -None of the _class-only_ tests above can answer key questions about how the -components actually behave on screen. - -- Is `Lightswitch.clicked()` bound to anything such that the user can invoke it? -- Is the `Lightswitch.message` displayed? -- Can the user actually select the hero displayed by `DashboardHeroComponent`? -- Is the hero name displayed as expected (i.e, in uppercase)? -- Is the welcome message displayed by the template of `WelcomeComponent`? - -These may not be troubling questions for the simple components illustrated above. -But many components have complex interactions with the DOM elements -described in their templates, causing HTML to appear and disappear as -the component state changes. - -To answer these kinds of questions, you have to create the DOM elements associated -with the components, you must examine the DOM to confirm that component state -displays properly at the appropriate times, and you must simulate user interaction -with the screen to determine whether those interactions cause the component to -behave as expected. - -To write these kinds of test, you'll use additional features of the `TestBed` -as well as other testing helpers. - -#### CLI-generated tests - -The CLI creates an initial test file for you by default when you ask it to -generate a new component. - -For example, the following CLI command generates a `BannerComponent` in the `app/banner` folder (with inline template and styles): - - -ng generate component banner --inline-template --inline-style --module app - - -It also generates an initial test file for the component, `banner-external.component.spec.ts`, that looks like this: - - - -
- -Because `compileComponents` is asynchronous, it uses -the [`async`](api/core/testing/async) utility -function imported from `@angular/core/testing`. - -Please refer to the [async](#async) section for more details. - -
- -#### Reduce the setup - -Only the last three lines of this file actually test the component -and all they do is assert that Angular can create the component. - -The rest of the file is boilerplate setup code anticipating more advanced tests that _might_ become necessary if the component evolves into something substantial. - -You'll learn about these advanced test features below. -For now, you can radically reduce this test file to a more manageable size: - - - -In this example, the metadata object passed to `TestBed.configureTestingModule` -simply declares `BannerComponent`, the component to test. - - - - -
- -There's no need to declare or import anything else. -The default test module is pre-configured with -something like the `BrowserModule` from `@angular/platform-browser`. - -Later you'll call `TestBed.configureTestingModule()` with -imports, providers, and more declarations to suit your testing needs. -Optional `override` methods can further fine-tune aspects of the configuration. - -
- -{@a create-component} - -#### _createComponent()_ - -After configuring `TestBed`, you call its `createComponent()` method. - - - - -`TestBed.createComponent()` creates an instance of the `BannerComponent`, -adds a corresponding element to the test-runner DOM, -and returns a [`ComponentFixture`](#component-fixture). - -
- -Do not re-configure `TestBed` after calling `createComponent`. - -The `createComponent` method freezes the current `TestBed` definition, -closing it to further configuration. - -You cannot call any more `TestBed` configuration methods, not `configureTestingModule()`, -nor `get()`, nor any of the `override...` methods. -If you try, `TestBed` throws an error. - -
- -{@a component-fixture} - -#### _ComponentFixture_ - -The [ComponentFixture](api/core/testing/ComponentFixture) is a test harness for interacting with the created component and its corresponding element. - -Access the component instance through the fixture and confirm it exists with a Jasmine expectation: - - - - -#### _beforeEach()_ - -You will add more tests as this component evolves. -Rather than duplicate the `TestBed` configuration for each test, -you refactor to pull the setup into a Jasmine `beforeEach()` and some supporting variables: - - - -Now add a test that gets the component's element from `fixture.nativeElement` and -looks for the expected text. - - - - -{@a native-element} - -#### _nativeElement_ - -The value of `ComponentFixture.nativeElement` has the `any` type. -Later you'll encounter the `DebugElement.nativeElement` and it too has the `any` type. - -Angular can't know at compile time what kind of HTML element the `nativeElement` is or -if it even is an HTML element. -The app might be running on a _non-browser platform_, such as the server or a -[Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API), -where the element may have a diminished API or not exist at all. - -The tests in this guide are designed to run in a browser so a -`nativeElement` value will always be an `HTMLElement` or -one of its derived classes. - -Knowing that it is an `HTMLElement` of some sort, you can use -the standard HTML `querySelector` to dive deeper into the element tree. - -Here's another test that calls `HTMLElement.querySelector` to get the paragraph element and look for the banner text: - - - - -{@a debug-element} - -#### _DebugElement_ - -The Angular _fixture_ provides the component's element directly through the `fixture.nativeElement`. - - - - -This is actually a convenience method, implemented as `fixture.debugElement.nativeElement`. - - - - -There's a good reason for this circuitous path to the element. - -The properties of the `nativeElement` depend upon the runtime environment. -You could be running these tests on a _non-browser_ platform that doesn't have a DOM or -whose DOM-emulation doesn't support the full `HTMLElement` API. - -Angular relies on the `DebugElement` abstraction to work safely across _all supported platforms_. -Instead of creating an HTML element tree, Angular creates a `DebugElement` tree that wraps the _native elements_ for the runtime platform. -The `nativeElement` property unwraps the `DebugElement` and returns the platform-specific element object. - -Because the sample tests for this guide are designed to run only in a browser, -a `nativeElement` in these tests is always an `HTMLElement` -whose familiar methods and properties you can explore within a test. - -Here's the previous test, re-implemented with `fixture.debugElement.nativeElement`: - - - - -The `DebugElement` has other methods and properties that -are useful in tests, as you'll see elsewhere in this guide. - -You import the `DebugElement` symbol from the Angular core library. - - - - -{@a by-css} -#### _By.css()_ - -Although the tests in this guide all run in the browser, -some apps might run on a different platform at least some of the time. - -For example, the component might render first on the server as part of a strategy to make the application launch faster on poorly connected devices. The server-side renderer might not support the full HTML element API. -If it doesn't support `querySelector`, the previous test could fail. - -The `DebugElement` offers query methods that work for all supported platforms. -These query methods take a _predicate_ function that returns `true` when a node in the `DebugElement` tree matches the selection criteria. - -You create a _predicate_ with the help of a `By` class imported from a -library for the runtime platform. Here's the `By` import for the browser platform: - - - - -The following example re-implements the previous test with -`DebugElement.query()` and the browser's `By.css` method. - - - - -Some noteworthy observations: - -- The `By.css()` static method selects `DebugElement` nodes - with a [standard CSS selector](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_started/Selectors 'CSS selectors'). -- The query returns a `DebugElement` for the paragraph. -- You must unwrap that result to get the paragraph element. - -When you're filtering by CSS selector and only testing properties of a browser's _native element_, the `By.css` approach may be overkill. - -It's often easier and more clear to filter with a standard `HTMLElement` method -such as `querySelector()` or `querySelectorAll()`, -as you'll see in the next set of tests. - -
- -## Component Test Scenarios - -The following sections, comprising most of this guide, explore common -component testing scenarios - -### Component binding - -The current `BannerComponent` presents static title text in the HTML template. - -After a few changes, the `BannerComponent` presents a dynamic title by binding to -the component's `title` property like this. - - - -Simple as this is, you decide to add a test to confirm that component -actually displays the right content where you think it should. - -#### Query for the _<h1>_ - -You'll write a sequence of tests that inspect the value of the `

` element -that wraps the _title_ property interpolation binding. - -You update the `beforeEach` to find that element with a standard HTML `querySelector` -and assign it to the `h1` variable. - - - -{@a detect-changes} - -#### _createComponent()_ does not bind data - -For your first test you'd like to see that the screen displays the default `title`. -Your instinct is to write a test that immediately inspects the `

` like this: - - - - -_That test fails_ with the message: - -```javascript -expected '' to contain 'Test Tour of Heroes'. -``` - -Binding happens when Angular performs **change detection**. - -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; a fact confirmed in the revised test: - - - -#### _detectChanges()_ - -You must tell the `TestBed` to perform data binding by calling `fixture.detectChanges()`. -Only then does the `

` have the expected title. - - - - -Delayed change detection is intentional and useful. -It gives the tester an opportunity to inspect and change the state of -the component _before Angular initiates data binding and calls [lifecycle hooks](guide/lifecycle-hooks)_. - -Here's another test that changes the component's `title` property _before_ calling `fixture.detectChanges()`. - - - - -{@a auto-detect-changes} - -#### Automatic change detection - -The `BannerComponent` tests frequently call `detectChanges`. -Some testers prefer that the Angular test environment run change detection automatically. - -That's possible by configuring the `TestBed` with the `ComponentFixtureAutoDetect` provider. -First import it from the testing utility library: - - - -Then add it to the `providers` array of the testing module configuration: - - - -Here are three tests that illustrate how automatic change detection works. - - - -The first test shows the benefit of automatic change detection. - -The second and third test reveal an important limitation. -The Angular testing environment does _not_ know that the test changed the component's `title`. -The `ComponentFixtureAutoDetect` service responds to _asynchronous activities_ such as promise resolution, timers, and DOM events. -But a direct, synchronous update of the component property is invisible. -The test must call `fixture.detectChanges()` manually to trigger another cycle of change detection. - -
- -Rather than wonder when the test fixture will or won't perform change detection, -the samples in this guide _always call_ `detectChanges()` _explicitly_. -There is no harm in calling `detectChanges()` more often than is strictly necessary. - -
- -
- -{@a dispatch-event} - -#### Change an input value with _dispatchEvent()_ - -To simulate user input, you can find the input element and set its `value` property. - -You will call `fixture.detectChanges()` to trigger Angular's change detection. -But there is an essential, intermediate step. - -Angular doesn't know that you set the input element's `value` property. -It won't read that property until you raise the element's `input` event by calling `dispatchEvent()`. -_Then_ you call `detectChanges()`. - -The following example demonstrates the proper sequence. - - - -
- -### Component with external files - -The `BannerComponent` above is defined with an _inline template_ and _inline css_, specified in the `@Component.template` and `@Component.styles` properties respectively. - -Many components specify _external templates_ and _external css_ with the -`@Component.templateUrl` and `@Component.styleUrls` properties respectively, -as the following variant of `BannerComponent` does. - - - -This syntax tells the Angular compiler to read the external files during component compilation. - -That's not a problem when you run the CLI `ng test` command because it -_compiles the app before running the tests_. - -However, if you run the tests in a **non-CLI environment**, -tests of this component may fail. -For example, if you run the `BannerComponent` tests in a web coding environment such as [plunker](https://plnkr.co/), you'll see a message like this one: - - -Error: This test module uses the component BannerComponent -which is using a "templateUrl" or "styleUrls", but they were never compiled. -Please call "TestBed.compileComponents" before your test. - - -You get this test failure message when the runtime environment -compiles the source code _during the tests themselves_. - -To correct the problem, call `compileComponents()` as explained [below](#compile-components). - -{@a component-with-dependency} - -### 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`: - - - -The `WelcomeComponent` has decision logic that interacts with the service, logic that makes this component worth testing. -Here's the testing module configuration for the spec file, `app/welcome/welcome.component.spec.ts`: - - - -This time, in addition to declaring the _component-under-test_, -the configuration adds a `UserService` provider to the `providers` list. -But not the real `UserService`. - -{@a service-test-doubles} - -#### 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 ask the user for login credentials and -attempt to reach an authentication server. -These behaviors can be hard to intercept. -It is far easier and safer to create and register a test double in place of the real `UserService`. - -This particular test suite supplies a minimal mock of the `UserService` that satisfies the needs of the `WelcomeComponent` and its tests: - - - -{@a get-injected-service} - -#### Get injected services - -The tests need access to the (stub) `UserService` injected into the `WelcomeComponent`. - -Angular has a hierarchical injection system. -There can be injectors at multiple levels, from the root injector created by the `TestBed` -down through the component tree. - -The safest way to get the injected service, the way that **_always works_**, -is to **get it from the injector of the _component-under-test_**. -The component injector is a property of the fixture's `DebugElement`. - - - - -{@a testbed-inject} - -#### _TestBed.inject()_ - -You _may_ also be able to get the service from the root injector via `TestBed.inject()`. -This is easier to remember and less verbose. -But it only works when Angular injects the component with the service instance in the test's root injector. - -In this test suite, the _only_ provider of `UserService` is the root testing module, -so it is safe to call `TestBed.inject()` as follows: - - - - -
- -For a use case in which `TestBed.inject()` does not work, -see the [_Override component providers_](#component-override) section that -explains when and why you must get the service from the component's injector instead. - -
- -{@a welcome-spec-setup} - -#### Final setup and tests - -Here's the complete `beforeEach()`, using `TestBed.inject()`: - - - -And here are some tests: - - - -The first is a sanity test; it confirms that the stubbed `UserService` is called and working. - -
- -The second parameter to the Jasmine matcher (e.g., `'expected name'`) is an optional failure label. -If the expectation fails, Jasmine appends this label to the expectation failure message. -In a spec with multiple expectations, it can help clarify what went wrong and which expectation failed. - -
- -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 component-with-async-service} - -### Component with async service - -In this sample, the `AboutComponent` template hosts a `TwainComponent`. -The `TwainComponent` displays Mark Twain quotes. - - - -Note that the value of the component's `quote` property passes through an `AsyncPipe`. -That means the property returns either a `Promise` or an `Observable`. - -In this example, the `TwainComponent.getQuote()` method tells you that -the `quote` property returns an `Observable`. - - - -The `TwainComponent` gets quotes from an injected `TwainService`. -The component starts the returned `Observable` with a placeholder value (`'...'`), -before the service can return its first quote. - -The `catchError` intercepts service errors, prepares an error message, -and returns the placeholder value on the success channel. -It must wait a tick to set the `errorMessage` -in order to avoid updating that message twice in the same change detection cycle. - -These are all features you'll want to test. - -#### Testing with a spy - -When testing a component, only the service's public API should matter. -In general, tests themselves should not make calls to remote servers. -They should emulate such calls. The setup in this `app/twain/twain.component.spec.ts` shows one way to do that: - - - -{@a service-spy} - -Focus on the spy. - - - - -The spy is designed such that any call to `getQuote` receives an observable with a test quote. -Unlike the real `getQuote()` method, this spy bypasses the server -and returns a synchronous observable whose value is available immediately. - -You can write many useful tests with this spy, even though its `Observable` is synchronous. - -{@a sync-tests} - -#### Synchronous tests - -A key advantage of a synchronous `Observable` is that -you can often turn asynchronous processes into synchronous tests. - - - - -Because the spy result returns synchronously, the `getQuote()` method updates -the message on screen immediately _after_ -the first change detection cycle during which Angular calls `ngOnInit`. - -You're not so lucky when testing the error path. -Although the service spy will return an error synchronously, -the component method calls `setTimeout()`. -The test must wait at least one full turn of the JavaScript engine before the -value becomes available. The test must become _asynchronous_. - -{@a fake-async} - -#### Async test with _fakeAsync()_ - -To use `fakeAsync()` functionality, you must import `zone.js/dist/zone-testing` in your test setup file. -If you created your project with the Angular CLI, `zone-testing` is already imported in `src/test.ts`. - -The following test confirms the expected behavior when the service returns an `ErrorObservable`. - - - - -Note that the `it()` function receives an argument of the following form. - -```javascript -fakeAsync(() => { /* test body */ }) -``` - -The `fakeAsync()` function enables a linear coding style by running the test body in a special `fakeAsync test zone`. -The test body appears to be synchronous. -There is no nested syntax (like a `Promise.then()`) to disrupt the flow of control. - -
- -Limitation: The `fakeAsync()` function won't work if the test body makes an `XMLHttpRequest` (XHR) call. -XHR calls within a test are rare, but if you need to call XHR, see [`async()`](#async), below. - -
- -{@a tick} - -#### The _tick()_ function - -You do have to call [tick()](api/core/testing/tick) to advance the (virtual) clock. - -Calling [tick()](api/core/testing/tick) simulates the passage of time until all pending asynchronous activities finish. -In this case, it waits for the error handler's `setTimeout()`. - -The [tick()](api/core/testing/tick) function accepts milliseconds and tickOptions as parameters, the millisecond (defaults to 0 if not provided) parameter represents how much the virtual clock advances. For example, if you have a `setTimeout(fn, 100)` in a `fakeAsync()` test, you need to use tick(100) to trigger the fn callback. The tickOptions is an optional parameter with a property called `processNewMacroTasksSynchronously` (defaults to true) represents whether to invoke new generated macro tasks when ticking. - - - - -The [tick()](api/core/testing/tick) function is one of the Angular testing utilities that you import with `TestBed`. -It's a companion to `fakeAsync()` and you can only call it within a `fakeAsync()` body. - -#### tickOptions - - - - -In this example, we have a new macro task (nested setTimeout), by default, when we `tick`, the setTimeout `outside` and `nested` will both be triggered. - - - - -And in some case, we don't want to trigger the new macro task when ticking, we can use `tick(milliseconds, {processNewMacroTasksSynchronously: false})` to not invoke new macro task. - -#### Comparing dates inside fakeAsync() - -`fakeAsync()` simulates passage of time, which allows you to calculate the difference between dates inside `fakeAsync()`. - - - - -#### jasmine.clock with fakeAsync() - -Jasmine also provides a `clock` feature to mock dates. Angular automatically runs tests that are run after -`jasmine.clock().install()` is called inside a `fakeAsync()` method until `jasmine.clock().uninstall()` is called. `fakeAsync()` is not needed and throws an error if nested. - -By default, this feature is disabled. To enable it, set a global flag before importing `zone-testing`. - -If you use the Angular CLI, configure this flag in `src/test.ts`. - -``` -(window as any)['__zone_symbol__fakeAsyncPatchLock'] = true; -import 'zone.js/dist/zone-testing'; -``` - - - - -#### Using the RxJS scheduler inside fakeAsync() - -You can also use RxJS scheduler in `fakeAsync()` just like using `setTimeout()` or `setInterval()`, but you need to import `zone.js/dist/zone-patch-rxjs-fake-async` to patch RxJS scheduler. - - - -#### Support more macroTasks - -By default, `fakeAsync()` supports the following macro tasks. - -- `setTimeout` -- `setInterval` -- `requestAnimationFrame` -- `webkitRequestAnimationFrame` -- `mozRequestAnimationFrame` - -If you run other macro tasks such as `HTMLCanvasElement.toBlob()`, an _"Unknown macroTask scheduled in fake async test"_ error will be thrown. - - - - - - - - -If you want to support such a case, you need to define the macro task you want to support in `beforeEach()`. -For example: - - - - -Note that in order to make the `` element Zone.js-aware in your app, you need to import the `zone-patch-canvas` patch (either in `polyfills.ts` or in the specific file that uses ``): - - - - - -#### Async observables - -You might be satisfied with the test coverage of these tests. - -However, you might be troubled by the fact that the real service doesn't quite behave this way. -The real service sends requests to a remote server. -A server takes time to respond and the response certainly won't be available immediately -as in the previous two tests. - -Your tests will reflect the real world more faithfully if you return an _asynchronous_ observable -from the `getQuote()` spy like this. - - - - -#### Async observable helpers - -The async observable was produced by an `asyncData` helper. -The `asyncData` helper is a utility function that you'll have to write yourself, or you can copy this one from the sample code. - - - - -This helper's observable emits the `data` value in the next turn of the JavaScript engine. - -The [RxJS `defer()` operator](http://reactivex.io/documentation/operators/defer.html) returns an observable. -It takes a factory function that returns either a promise or an observable. -When something subscribes to _defer_'s observable, -it adds the subscriber to a new observable created with that factory. - -The `defer()` operator transforms the `Promise.resolve()` into a new observable that, -like `HttpClient`, emits once and completes. -Subscribers are unsubscribed after they receive the data value. - -There's a similar helper for producing an async error. - - - - -#### More async tests - -Now that the `getQuote()` spy is returning async observables, -most of your tests will have to be async as well. - -Here's a `fakeAsync()` test that demonstrates the data flow you'd expect -in the real world. - - - - -Notice that the quote element displays the placeholder value (`'...'`) after `ngOnInit()`. -The first quote hasn't arrived yet. - -To flush the first quote from the observable, you call [tick()](api/core/testing/tick). -Then call `detectChanges()` to tell Angular to update the screen. - -Then you can assert that the quote element displays the expected text. - -{@a async} - -#### Async test with _async()_ - -To use `async()` functionality, you must import `zone.js/dist/zone-testing` in your test setup file. -If you created your project with the Angular CLI, `zone-testing` is already imported in `src/test.ts`. - -The `fakeAsync()` utility function has a few limitations. -In particular, it won't work if the test body makes an `XMLHttpRequest` (XHR) call. -XHR calls within a test are rare so you can generally stick with [`fakeAsync()`](#fake-async). -But if you ever do need to call `XMLHttpRequest`, you'll want to know about `async()`. - -
- -The `TestBed.compileComponents()` method (see [below](#compile-components)) calls `XHR` -to read external template and css files during "just-in-time" compilation. -Write tests that call `compileComponents()` with the `async()` utility. - -
- -Here's the previous `fakeAsync()` test, re-written with the `async()` utility. - - - - -The `async()` utility hides some asynchronous boilerplate by arranging for the tester's code -to run in a special _async test zone_. -You don't need to pass Jasmine's `done()` into the test and call `done()` because it is `undefined` in promise or observable callbacks. - -But the test's asynchronous nature is revealed by the call to `fixture.whenStable()`, -which breaks the linear flow of control. - -When using an `intervalTimer()` such as `setInterval()` in `async()`, remember to cancel the timer with `clearInterval()` after the test, otherwise the `async()` never ends. - -{@a when-stable} - -#### _whenStable_ - -The test must wait for the `getQuote()` observable to emit the next quote. -Instead of calling [tick()](api/core/testing/tick), it calls `fixture.whenStable()`. - -The `fixture.whenStable()` returns a promise that resolves when the JavaScript engine's -task queue becomes empty. -In this example, the task queue becomes empty when the observable emits the first quote. - -The test resumes within the promise callback, which calls `detectChanges()` to -update the quote element with the expected text. - -{@a jasmine-done} - -#### Jasmine _done()_ - -While the `async()` and `fakeAsync()` functions greatly -simplify Angular asynchronous testing, -you can still fall back to the traditional technique -and pass `it` a function that takes a -[`done` callback](https://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support). - -You can't call `done()` in `async()` or `fakeAsync()` functions, because the `done parameter` -is `undefined`. - -Now you are responsible for chaining promises, handling errors, and calling `done()` at the appropriate moments. - -Writing test functions with `done()`, is more cumbersome than `async()`and `fakeAsync()`, but it is occasionally necessary when code involves the `intervalTimer()` like `setInterval`. - -Here are two more versions of the previous test, written with `done()`. -The first one subscribes to the `Observable` exposed to the template by the component's `quote` property. - - - -The RxJS `last()` operator emits the observable's last value before completing, which will be the test quote. -The `subscribe` callback calls `detectChanges()` to -update the quote element with the test quote, in the same manner as the earlier tests. - -In some tests, you're more interested in how an injected service method was called and what values it returned, -than what appears on screen. - -A service spy, such as the `qetQuote()` spy of the fake `TwainService`, -can give you that information and make assertions about the state of the view. - - - -
- -{@a marble-testing} -### Component marble tests - -The previous `TwainComponent` tests simulated an asynchronous observable response -from the `TwainService` with the `asyncData` and `asyncError` utilities. - -These are short, simple functions that you can write yourself. -Unfortunately, they're too simple for many common scenarios. -An observable often emits multiple times, perhaps after a significant delay. -A component may coordinate multiple observables -with overlapping sequences of values and errors. - -**RxJS marble testing** is a great way to test observable scenarios, -both simple and complex. -You've likely seen the [marble diagrams](http://rxmarbles.com/) -that illustrate how observables work. -Marble testing uses a similar marble language to -specify the observable streams and expectations in your tests. - -The following examples revisit two of the `TwainComponent` tests -with marble testing. - -Start by installing the `jasmine-marbles` npm package. -Then import the symbols you need. - - - -Here's the complete test for getting a quote: - - - -Notice that the Jasmine test is synchronous. There's no `fakeAsync()`. -Marble testing uses a test scheduler to simulate the passage of time -in a synchronous test. - -The beauty of marble testing is in the visual definition of the observable streams. -This test defines a [_cold_ observable](#cold-observable) that waits -three [frames](#marble-frame) (`---`), -emits a value (`x`), and completes (`|`). -In the second argument you map the value marker (`x`) to the emitted value (`testQuote`). - - - -The marble library constructs the corresponding observable, which the -test sets as the `getQuote` spy's return value. - -When you're ready to activate the marble observables, -you tell the `TestScheduler` to _flush_ its queue of prepared tasks like this. - - - -This step serves a purpose analogous to [tick()](api/core/testing/tick) and `whenStable()` in the -earlier `fakeAsync()` and `async()` examples. -The balance of the test is the same as those examples. - -#### Marble error testing - -Here's the marble testing version of the `getQuote()` error test. - - - -It's still an async test, calling `fakeAsync()` and [tick()](api/core/testing/tick), because the component itself -calls `setTimeout()` when processing errors. - -Look at the marble observable definition. - - - -This is a _cold_ observable that waits three frames and then emits an error, -The hash (`#`) indicates the timing of the error that is specified in the third argument. -The second argument is null because the observable never emits a value. - -#### Learn about marble testing - -{@a marble-frame} -A _marble frame_ is a virtual unit of testing time. -Each symbol (`-`, `x`, `|`, `#`) marks the passing of one frame. - -{@a cold-observable} -A _cold_ observable doesn't produce values until you subscribe to it. -Most of your application observables are cold. -All [_HttpClient_](guide/http) methods return cold observables. - -A _hot_ observable is already producing values _before_ you subscribe to it. -The [_Router.events_](api/router/Router#events) observable, -which reports router activity, is a _hot_ observable. - -RxJS marble testing is a rich subject, beyond the scope of this guide. -Learn about it on the web, starting with the -[official documentation](https://github.com/ReactiveX/rxjs/blob/master/doc/writing-marble-tests.md). - -
- -{@a component-with-input-output} - -### 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 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 a tiny example of a component in this role. -It displays an individual hero provided by the `DashboardComponent`. -Clicking that hero tells the `DashboardComponent` that the user has selected the hero. - -The `DashboardHeroComponent` is embedded in the `DashboardComponent` template like this: - - - -The `DashboardHeroComponent` appears in an `*ngFor` repeater, which sets each component's `hero` input property -to the looping value and listens for the component's `selected` event. - -Here's the component's full definition: - -{@a dashboard-hero-component} - - - -While testing a component this simple has little intrinsic value, it's worth knowing how. -You can use one of these approaches: - -- Test it as used by `DashboardComponent`. -- Test it as a stand-alone component. -- Test it as used by a substitute for `DashboardComponent`. - -A quick look at the `DashboardComponent` constructor discourages the first approach: - - - -The `DashboardComponent` depends on the Angular router and the `HeroService`. -You'd probably have to replace them both with test doubles, which is a lot of work. -The router seems particularly challenging. - -
- -The [discussion below](#routing-component) covers testing components that require the router. - -
- -The immediate goal is to test the `DashboardHeroComponent`, not the `DashboardComponent`, -so, try the second and third options. - -{@a dashboard-standalone} - -#### Test _DashboardHeroComponent_ stand-alone - -Here's the meat of the spec file setup. - - - -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 following test verifies that the hero name is propagated to the template via a binding. - - - - -Because the [template](#dashboard-hero-component) passes the hero name through the Angular `UpperCasePipe`, -the test must match the element value with the upper-cased name. - -
- -This small test demonstrates how Angular tests can verify a component's visual -representation—something not possible with -[component class tests](#component-class-testing)—at -low cost and without resorting to much slower and more complicated end-to-end tests. - -
- -#### Clicking - -Clicking the hero should raise a `selected` event that -the host component (`DashboardComponent` presumably) can hear: - - - - -The component's `selected` property returns an `EventEmitter`, -which looks like an RxJS synchronous `Observable` to consumers. -The test subscribes to it _explicitly_ just as the host component does _implicitly_. - -If the component behaves as expected, clicking the hero's element -should tell the component's `selected` property to emit the `hero` object. - -The test detects that event through its subscription to `selected`. - -{@a trigger-event-handler} - -#### _triggerEventHandler_ - -The `heroDe` in the previous test is a `DebugElement` that represents the hero `
`. - -It has Angular properties and methods that abstract interaction with the native element. -This test calls the `DebugElement.triggerEventHandler` with the "click" event name. -The "click" event binding responds by calling `DashboardHeroComponent.click()`. - -The Angular `DebugElement.triggerEventHandler` can raise _any data-bound event_ by its _event name_. -The second parameter is the event object passed to the handler. - -The test triggered a "click" event with a `null` event object. - - - - -The test assumes (correctly in this case) that the runtime -event handler—the component's `click()` method—doesn't -care about the event object. - -
- -Other handlers are less forgiving. For example, the `RouterLink` -directive expects an object with a `button` property -that identifies which mouse button (if any) was pressed during the click. -The `RouterLink` directive throws an error if the event object is missing. - -
- -#### Click the element - -The following test alternative calls the native element's own `click()` method, -which is perfectly fine for _this component_. - - - - -{@a click-helper} - -#### _click()_ helper - -Clicking a button, an anchor, or an arbitrary HTML element is a common test task. - -Make that consistent and easy by encapsulating the _click-triggering_ process -in a helper such as the `click()` function below: - - - -The first parameter is the _element-to-click_. If you wish, you can pass a -custom event object as the second parameter. The default is a (partial) -
left-button mouse event object -accepted by many handlers including the `RouterLink` directive. - -
- -The `click()` helper function is **not** one of the Angular testing utilities. -It's a function defined in _this guide's sample code_. -All of the sample tests use it. -If you like it, add it to your own collection of helpers. - -
- -Here's the previous test, rewritten using the click helper. - - - - -
- -{@a component-inside-test-host} - -### Component inside a test host - -The previous tests played the role of the host `DashboardComponent` themselves. -But does the `DashboardHeroComponent` work correctly when properly data-bound to a host component? - -You could test with the actual `DashboardComponent`. -But doing so could require a lot of setup, -especially when its template features an `*ngFor` repeater, -other components, layout HTML, additional bindings, -a constructor that injects multiple services, -and it starts interacting with those services right away. - -Imagine the effort to disable these distractions, just to prove a point -that can be made satisfactorily with a _test host_ like this one: - - - -This test host binds to `DashboardHeroComponent` as the `DashboardComponent` would -but without the noise of the `Router`, the `HeroService`, or 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, -which records the emitted hero in its `selectedHero` property. - -Later, the tests will be able to easily check `selectedHero` to verify that the -`DashboardHeroComponent.selected` event emitted the expected hero. - -The setup for the _test-host_ tests is similar to the setup for the stand-alone tests: - - - -This testing module configuration shows three important differences: - -1. It _declares_ both the `DashboardHeroComponent` and the `TestHostComponent`. -1. It _creates_ the `TestHostComponent` instead of the `DashboardHeroComponent`. -1. The `TestHostComponent` sets the `DashboardHeroComponent.hero` with a binding. - -The `createComponent` returns a `fixture` that holds an instance of `TestHostComponent` instead of an instance of `DashboardHeroComponent`. - -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: - - - -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 routing-component} - -### Routing component - -A _routing component_ is a component that tells the `Router` to navigate to another component. -The `DashboardComponent` is a _routing component_ because the user can -navigate to the `HeroDetailComponent` by clicking on one of the _hero buttons_ on the dashboard. - -Routing is pretty complicated. -Testing the `DashboardComponent` seemed daunting in part because it involves the `Router`, -which it injects together with the `HeroService`. - - - -Mocking the `HeroService` with a spy is a [familiar story](#component-with-async-service). -But the `Router` has a complicated API and is entwined with other services and application preconditions. Might it be difficult to mock? - -Fortunately, not in this case because the `DashboardComponent` isn't doing much with the `Router` - - - - -This is often the case with _routing components_. -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. - -Providing a router spy for _this component_ test suite happens to be as easy -as providing a `HeroService` spy. - - - -The following test clicks the displayed hero and confirms that -`Router.navigateByUrl` is called with the expected url. - - - -{@a routed-component-w-param} - -### Routed components - -A _routed component_ is the destination of a `Router` navigation. -It can be trickier to test, especially when the route to the component _includes parameters_. -The `HeroDetailComponent` is a _routed component_ that is the destination of such a route. - -When a user clicks a _Dashboard_ hero, the `DashboardComponent` tells the `Router` -to navigate to `heroes/:id`. -The `:id` is a route parameter whose value is the `id` of the hero to edit. - -The `Router` matches that URL to a route to the `HeroDetailComponent`. -It creates an `ActivatedRoute` object with the routing information and -injects it into a new instance of the `HeroDetailComponent`. - -Here's the `HeroDetailComponent` constructor: - - - -The `HeroDetail` component needs the `id` parameter so it can fetch -the corresponding hero via the `HeroDetailService`. -The component has to get the `id` from the `ActivatedRoute.paramMap` property -which is an `Observable`. - -It can't just reference the `id` property of the `ActivatedRoute.paramMap`. -The component has to _subscribe_ to the `ActivatedRoute.paramMap` observable and be prepared -for the `id` to change during its lifetime. - - - -
- -The [ActivatedRoute in action](guide/router#activated-route-in-action) section of the [Router](guide/router) guide covers `ActivatedRoute.paramMap` in more detail. - -
- -Tests can explore how the `HeroDetailComponent` responds to different `id` parameter values -by manipulating the `ActivatedRoute` injected into the component's constructor. - -You know how to spy on the `Router` and a data service. - -You'll take a different approach with `ActivatedRoute` because - -- `paramMap` returns an `Observable` that can emit more than one value - during a test. -- You need the router helper function, `convertToParamMap()`, to create a `ParamMap`. -- Other _routed component_ tests need a test double for `ActivatedRoute`. - -These differences argue for a re-usable stub class. - -#### _ActivatedRouteStub_ - -The following `ActivatedRouteStub` class serves as a test double for `ActivatedRoute`. - - - -Consider placing such helpers in a `testing` folder sibling to the `app` folder. -This sample puts `ActivatedRouteStub` in `testing/activated-route-stub.ts`. - -
- -Consider writing a more capable version of this stub class with -the [_marble testing library_](#marble-testing). - -
- -{@a tests-w-test-double} - -#### Testing with _ActivatedRouteStub_ - -Here's a test demonstrating the component's behavior when the observed `id` refers to an existing hero: - - - -
- -The `createComponent()` method and `page` object are discussed [below](#page-object). -Rely on your intuition for now. - -
- -When the `id` cannot be found, the component should re-route to the `HeroListComponent`. - -The test suite setup provided the same router spy [described above](#routing-component) which spies on the router without actually navigating. - -This test expects the component to try to navigate to the `HeroListComponent`. - - - -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: - - - -
- -### Nested component tests - -Component templates often have nested components, whose templates -may contain more components. - -The component tree can be very deep and, most of the time, the nested components -play no role in testing the component at the top of the tree. - -The `AppComponent`, for example, displays a navigation bar with anchors and their `RouterLink` directives. - - - -While the `AppComponent` _class_ is empty, -you may want to write unit tests to confirm that the links are wired properly -to the `RouterLink` directives, perhaps for the reasons [explained below](#why-stubbed-routerlink-tests). - -To validate the links, you don't need the `Router` to navigate and you don't -need the `` to mark where the `Router` inserts _routed components_. - -The `BannerComponent` and `WelcomeComponent` -(indicated by `` and ``) are also irrelevant. - -Yet any test that creates the `AppComponent` in the DOM will also create instances of -these three components and, if you let that happen, -you'll have to configure the `TestBed` to create them. - -If you neglect to declare them, the Angular compiler won't recognize the -``, ``, and `` tags in the `AppComponent` template -and will throw an error. - -If you declare the real components, you'll also have to declare _their_ nested components -and provide for _all_ services injected in _any_ component in the tree. - -That's too much effort just to answer a few simple questions about links. - -This section describes two techniques for minimizing the setup. -Use them, alone or in combination, to stay focused on the testing the primary component. - -{@a stub-component} - -##### Stubbing unneeded components - -In the first technique, you create and declare stub versions of the components -and directive that play little or no role in the tests. - - - -The stub selectors match the selectors for the corresponding real components. -But their templates and classes are empty. - -Then declare them in the `TestBed` configuration next to the -components, directives, and pipes that need to be real. - - - -The `AppComponent` is the test subject, so of course you declare the real version. - -The `RouterLinkDirectiveStub`, [described later](#routerlink), is a test version -of the real `RouterLink` that helps with the link tests. - -The rest are stubs. - -{@a no-errors-schema} - -#### _NO_ERRORS_SCHEMA_ - -In the second approach, add `NO_ERRORS_SCHEMA` to the `TestBed.schemas` metadata. - - - -The `NO_ERRORS_SCHEMA` tells the Angular compiler to ignore unrecognized elements and attributes. - -The compiler will recognize the `` element and the `routerLink` attribute -because you declared a corresponding `AppComponent` and `RouterLinkDirectiveStub` -in the `TestBed` configuration. - -But the compiler won't throw an error when it encounters ``, ``, or ``. -It simply renders them as empty tags and the browser ignores them. - -You no longer need the stub components. - -#### Use both techniques together - -These are techniques for _Shallow Component Testing_ , -so-named because they reduce the visual surface of the component to just those elements -in the component's template that matter for tests. - -The `NO_ERRORS_SCHEMA` approach is the easier of the two but don't overuse it. - -The `NO_ERRORS_SCHEMA` also prevents the compiler from telling you about the missing -components and attributes that you omitted inadvertently or misspelled. -You could waste hours chasing phantom bugs that the compiler would have caught in an instant. - -The _stub component_ approach has another advantage. -While the stubs in _this_ example were empty, -you could give them stripped-down templates and classes if your tests -need to interact with them in some way. - -In practice you will combine the two techniques in the same setup, -as seen in this example. - - - -The Angular compiler creates the `BannerComponentStub` for the `` element -and applies the `RouterLinkStubDirective` to the anchors with the `routerLink` attribute, -but it ignores the `` and `` tags. - -
- -{@a routerlink} -### Components with _RouterLink_ - -The real `RouterLinkDirective` is quite complicated and entangled with other components -and directives of the `RouterModule`. -It requires challenging setup to mock and use in tests. - -The `RouterLinkDirectiveStub` in this sample code replaces the real directive -with an alternative version designed to validate the kind of anchor tag wiring -seen in the `AppComponent` template. - - - -The URL bound to the `[routerLink]` attribute flows in to the directive's `linkParams` property. - -The `HostListener` wires the click event of the host element -(the `` anchor elements in `AppComponent`) to the stub directive's `onClick` method. - -Clicking the anchor should trigger the `onClick()` method, -which sets the stub's telltale `navigatedTo` property. -Tests inspect `navigatedTo` to confirm that clicking the anchor -set the expected route definition. - -
- -Whether the router is configured properly to navigate with that route definition is a -question for a separate set of tests. - -
- -{@a by-directive} -{@a inject-directive} - -#### _By.directive_ and injected directives - -A little more setup triggers the initial data binding and gets references to the navigation links: - - - -Three points of special interest: - -1. You can locate the anchor elements with an attached directive using `By.directive`. - -1. The query returns `DebugElement` wrappers around the matching elements. - -1. Each `DebugElement` exposes a dependency injector with the - specific instance of the directive attached to that element. - -The `AppComponent` links to validate are as follows: - - - -{@a app-component-tests} - -Here are some tests that confirm those links are wired to the `routerLink` directives -as expected: - - - -
- -The "click" test _in this example_ is misleading. -It tests the `RouterLinkDirectiveStub` rather than the _component_. -This is a common failing of directive stubs. - -It has a legitimate purpose in this guide. -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. - -
- -{@a why-stubbed-routerlink-tests} - -#### 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 guide update will explain how to write such -tests with the `RouterTestingModule`. - -
- -
- -{@a page-object} - -### Use a _page_ object - -The `HeroDetailComponent` is a simple view with a title, two hero fields, and two buttons. - - - -But there's plenty of template complexity even in this simple form. - - - -Tests that exercise the component need ... - -- to wait until a hero arrives before elements appear in the DOM. -- a reference to the title text. -- a reference to the name input box to inspect and set it. -- references to the two buttons so they can click them. -- spies for some of the component and router methods. - -Even a small form such as this one can produce a mess of tortured conditional setup and CSS element selection. - -Tame the complexity with a `Page` class that handles access to component properties -and encapsulates the logic that sets them. - -Here is such a `Page` class for the `hero-detail.component.spec.ts` - - - -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` object and fills in the blanks once the `hero` arrives. - - - -The [_HeroDetailComponent_ tests](#tests-w-test-double) in an earlier 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 reinforce the point. - - - -
- -{@a compile-components} -### Calling _compileComponents()_ -
- -You can ignore this section if you _only_ run tests with the CLI `ng test` command -because the CLI compiles the application before running the tests. - -
- -If you run tests in a **non-CLI environment**, the tests may fail with a message like this one: - - -Error: This test module uses the component BannerComponent -which is using a "templateUrl" or "styleUrls", but they were never compiled. -Please call "TestBed.compileComponents" before your test. - - -The root of the problem is at least one of the components involved in the test -specifies an external template or CSS file as -the following version of the `BannerComponent` does. - - - -The test fails when the `TestBed` tries to create the component. - - - -Recall that the app hasn't been compiled. -So when you call `createComponent()`, the `TestBed` compiles implicitly. - -That's not a problem when the source code is in memory. -But the `BannerComponent` requires external files -that the compiler must read from the file system, -an inherently _asynchronous_ operation. - -If the `TestBed` were allowed to continue, the tests would run and fail mysteriously -before the compiler could finished. - -The preemptive error message tells you to compile explicitly with `compileComponents()`. - -#### _compileComponents()_ is async - -You must call `compileComponents()` within an asynchronous test function. - -
- -If you neglect to make the test function async -(e.g., forget to use `async()` as described below), -you'll see this error message - - -Error: ViewDestroyedError: Attempt to use a destroyed view - - -
- -A typical approach is to divide the setup logic into two separate `beforeEach()` functions: - -1. An async `beforeEach()` that compiles the components -1. A synchronous `beforeEach()` that performs the remaining setup. - -To follow this pattern, import the `async()` helper with the other testing symbols. - - - - -#### The async _beforeEach_ - -Write the first async `beforeEach` like this. - - - -The `async()` helper function takes a parameterless function with the body of the setup. - -The `TestBed.configureTestingModule()` method returns the `TestBed` class so you can chain -calls to other `TestBed` static methods such as `compileComponents()`. - -In this example, the `BannerComponent` is the only component to compile. -Other examples configure the testing module with multiple components -and may import application modules that hold yet more components. -Any of them could be require external files. - -The `TestBed.compileComponents` method asynchronously compiles all components configured in the testing module. - -
- -Do not re-configure the `TestBed` after calling `compileComponents()`. - -
- -Calling `compileComponents()` closes the current `TestBed` instance to further configuration. -You cannot call any more `TestBed` configuration methods, not `configureTestingModule()` -nor any of the `override...` methods. The `TestBed` throws an error if you try. - -Make `compileComponents()` the last step -before calling `TestBed.createComponent()`. - -#### The synchronous _beforeEach_ - -The second, synchronous `beforeEach()` contains the remaining setup steps, -which include creating the component and querying for elements to inspect. - - - -You can count on the test runner to wait for the first asynchronous `beforeEach` to finish before calling the second. - -#### Consolidated setup - -You can consolidate the two `beforeEach()` functions into a single, async `beforeEach()`. - -The `compileComponents()` method returns a promise so you can perform the -synchronous setup tasks _after_ compilation by moving the synchronous code -into a `then(...)` callback. - - - -#### _compileComponents()_ is harmless - -There's no harm in calling `compileComponents()` when it's not required. - -The component test file generated by the CLI calls `compileComponents()` -even though it is never required when running `ng test`. - -The tests in this guide only call `compileComponents` when necessary. - -
- -{@a import-module} - -### Setup with module imports - -Earlier component tests configured the testing module with a few `declarations` like this: - - - - -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` to 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: - - - -
- -Notice that the `beforeEach()` is asynchronous and calls `TestBed.compileComponents` -because the `HeroDetailComponent` has an external template and css file. - -As explained in [_Calling compileComponents()_](#compile-components) above, -these tests could be run in a non-CLI environment -where Angular would have to compile them in the browser. - -
- -#### Import a shared module - -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: - - - -It's a bit tighter and smaller, with fewer import statements (not shown). - -{@a feature-module-import} - -#### Import a feature module - -The `HeroDetailComponent` is part of the `HeroModule` [Feature Module](guide/feature-modules) that aggregates more of the interdependent pieces -including the `SharedModule`. -Try a test configuration that imports the `HeroModule` like this one: - - - -That's _really_ crisp. Only the _test doubles_ in the `providers` remain. Even the `HeroDetailComponent` declaration is gone. - -In fact, if you try to declare it, Angular will throw an error because -`HeroDetailComponent` is declared in both the `HeroModule` and the `DynamicTestModule` -created by the `TestBed`. - -
- -Importing the component's feature module can be the easiest way to configure tests -when there are many mutual dependencies within the module and -the module is small, as feature modules tend to be. - -
- -
- -{@a component-override} - -### Override component providers - -The `HeroDetailComponent` provides its own `HeroDetailService`. - - - -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! - -
- -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`. - - - -The [previous test configuration](#feature-module-import) replaces the real `HeroService` with a `TestHeroService` -that intercepts server requests and fakes their responses. - -
- -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: - - - -Notice that `TestBed.configureTestingModule` no longer provides a (fake) `HeroService` because it's [not needed](#spy-stub). - -{@a override-component-method} - -#### The _overrideComponent_ method - -Focus on the `overrideComponent` method. - - - -It takes two arguments: the component type to override (`HeroDetailComponent`) and an override metadata object. -The [override metadata object](#metadata-override-object) is a generic defined as follows: - - - type MetadataOverride<T> = { - add?: Partial<T>; - remove?: Partial<T>; - set?: Partial<T>; - }; - - -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: - - - selector?: string; - template?: string; - templateUrl?: string; - providers?: any[]; - ... - - -{@a spy-stub} - -#### Provide a _spy stub_ (_HeroDetailServiceSpy_) - -This example completely replaces the component's `providers` array with a new array containing a `HeroDetailServiceSpy`. - -The `HeroDetailServiceSpy` is a stubbed version of the real `HeroDetailService` -that fakes all necessary features of that service. -It neither injects nor delegates to the lower level `HeroService` -so there's no need to provide a test double for that. - -The related `HeroDetailComponent` tests will assert that methods of the `HeroDetailService` -were called by spying on the service methods. -Accordingly, the stub implements its methods as spies: - - - -{@a override-tests} - -#### The override tests - -Now the tests can control the component's hero directly by manipulating the spy-stub's `testHero` -and confirm that service methods were called. - - - -{@a more-overrides} - -#### 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 attribute-directive} - -## Attribute Directive Testing - -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. - - - -It's used throughout the application, perhaps most simply in the `AboutComponent`: - - - -Testing the specific use of the `HighlightDirective` within the `AboutComponent` requires only the -techniques explored above (in particular the ["Shallow test"](#nested-component-tests) approach). - - - -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. - -_Class-only tests_ might be helpful, -but attribute directives like this one tend to manipulate the DOM. -Isolated unit tests don't touch the DOM and, therefore, -do not 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. - - - - - -
- -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. - -
- -Here are some tests of this component: - - - -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 second `

` to get its `HighlightDirective` instance - and its `defaultColor`. - -- `DebugElement.properties` affords access to the artificial custom property that is set by the directive. - -
- -## Pipe Testing - -Pipes are easy to test without the Angular testing utilities. - -A pipe class has one method, `transform`, that manipulates the input -value into a transformed output value. -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 with a regular expression. - - - -Anything that uses a regular expression is worth testing thoroughly. -Use simple Jasmine to explore the expected cases and the edge cases. - - - -{@a write-tests} - -#### Write DOM 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 component tests such as this one: - - - -
- -{@a test-debugging} - -## Test debugging - -Debug specs in the browser in the same way that you debug an application. - -1. Reveal the Karma browser window (hidden earlier). -1. Click the **DEBUG** button; it opens a new browser tab and re-runs the tests. -1. Open the browser's “Developer Tools” (`Ctrl-Shift-I` on Windows; `Command-Option-I` in macOS). -1. Pick the "sources" section. -1. Open the `1st.spec.ts` test file (Control/Command-P, then start typing the name of the file). -1. Set a breakpoint in the test. -1. Refresh the browser, and it stops at the breakpoint. - - - -
- -{@a atu-apis} - -## Testing Utility APIs - -This section takes inventory of the most useful Angular testing features and summarizes what they do. - -The Angular testing utilities include the `TestBed`, the `ComponentFixture`, and a handful of functions that control the test environment. -The [_TestBed_](#testbed-api-summary) and [_ComponentFixture_](#component-fixture-api-summary) classes are covered separately. - -Here's a summary of the stand-alone functions, in order of likely utility: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Function - - Description -
- async - - - Runs the body of a test (`it`) or setup (`beforeEach`) function within a special _async test zone_. - See [discussion above](#async). - -
- fakeAsync - - - Runs the body of a test (`it`) within a special _fakeAsync test zone_, enabling - a linear control flow coding style. See [discussion above](#fake-async). - -
- tick - - - Simulates the passage of time and the completion of pending asynchronous activities - by flushing both _timer_ and _micro-task_ queues within the _fakeAsync test zone_. - -
- - The curious, dedicated reader might enjoy this lengthy blog post, - ["_Tasks, microtasks, queues and schedules_"](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/). - -
- - Accepts an optional argument that moves the virtual clock forward - by the specified number of milliseconds, - clearing asynchronous activities scheduled within that timeframe. - See [discussion above](#tick). - -
- inject - - - Injects one or more services from the current `TestBed` injector into a test function. - It cannot inject a service provided by the component itself. - See discussion of the [debugElement.injector](#get-injected-services). - -
- discardPeriodicTasks - - - When a `fakeAsync()` test ends with pending timer event _tasks_ (queued `setTimeOut` and `setInterval` callbacks), - the test fails with a clear error message. - - In general, a test should end with no queued tasks. - When pending timer tasks are expected, call `discardPeriodicTasks` to flush the _task_ queue - and avoid the error. - -
- flushMicrotasks - - - When a `fakeAsync()` test ends with pending _micro-tasks_ such as unresolved promises, - the test fails with a clear error message. - - In general, a test should wait for micro-tasks to finish. - When pending microtasks are expected, call `flushMicrotasks` to flush the _micro-task_ queue - and avoid the error. - -
- ComponentFixtureAutoDetect - - - A provider token for a service that turns on [automatic change detection](#automatic-change-detection). - -
- getTestBed - - - Gets the current instance of the `TestBed`. - Usually unnecessary because the static class methods of the `TestBed` class are typically sufficient. - The `TestBed` instance exposes a few rarely used members that are not available as - static methods. - -
- -
- -{@a testbed-class-summary} - -#### _TestBed_ class summary - -The `TestBed` class is one of the principal Angular testing utilities. -Its API is quite large and can be overwhelming until you've explored it, -a little at a time. Read the early part of this guide first -to get the basics before trying to absorb the full API. - -The module definition passed to `configureTestingModule` -is a subset of the `@NgModule` metadata properties. - - - type TestModuleMetadata = { - providers?: any[]; - declarations?: any[]; - imports?: any[]; - schemas?: Array<SchemaMetadata | any[]>; - }; - - -{@a metadata-override-object} - -Each override method takes a `MetadataOverride` where `T` is the kind of metadata -appropriate to the method, that is, the parameter of an `@NgModule`, -`@Component`, `@Directive`, or `@Pipe`. - - - type MetadataOverride<T> = { - add?: Partial<T>; - remove?: Partial<T>; - set?: Partial<T>; - }; - - -{@a testbed-methods} -{@a testbed-api-summary} - -The `TestBed` API consists of static class methods that either update or reference a _global_ instance of the `TestBed`. - -Internally, all static methods cover methods of the current runtime `TestBed` instance, -which is also returned by the `getTestBed()` function. - -Call `TestBed` methods _within_ a `beforeEach()` to ensure a fresh start before each individual test. - -Here are the most important static methods, in order of likely utility. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Methods - - Description -
- configureTestingModule - - - The testing shims (`karma-test-shim`, `browser-test-shim`) - establish the [initial test environment](guide/testing) and a default testing module. - The default testing module is configured with basic declaratives and some Angular service substitutes that every tester needs. - - Call `configureTestingModule` to refine the testing module configuration for a particular set of tests - by adding and removing imports, declarations (of components, directives, and pipes), and providers. - -
- compileComponents - - - Compile the testing module asynchronously after you've finished configuring it. - You **must** call this method if _any_ of the testing module components have a `templateUrl` - or `styleUrls` because fetching component template and style files is necessarily asynchronous. - See [above](#compile-components). - - After calling `compileComponents`, the `TestBed` configuration is frozen for the duration of the current spec. - -
- createComponent - - - Create an instance of a component of type `T` based on the current `TestBed` configuration. - After calling `compileComponent`, the `TestBed` configuration is frozen for the duration of the current spec. - -
- overrideModule - - - Replace metadata for the given `NgModule`. Recall that modules can import other modules. - The `overrideModule` method can reach deeply into the current testing module to - modify one of these inner modules. - -
- overrideComponent - - - Replace metadata for the given component class, which could be nested deeply - within an inner module. - -
- overrideDirective - - - Replace metadata for the given directive class, which could be nested deeply - within an inner module. - -
- overridePipe - - - Replace metadata for the given pipe class, which could be nested deeply - within an inner module. - -
- {@a testbed-inject} - inject - - - Retrieve a service from the current `TestBed` injector. - - The `inject` function is often adequate for this purpose. - But `inject` throws an error if it can't provide the service. - - What if the service is optional? - - The `TestBed.inject()` method takes an optional second parameter, - the object to return if Angular can't find the provider - (`null` in this example): - - - - After calling `TestBed.inject`, the `TestBed` configuration is frozen for the duration of the current spec. - -
- {@a testbed-initTestEnvironment} - initTestEnvironment - - - Initialize the testing environment for the entire test run. - - The testing shims (`karma-test-shim`, `browser-test-shim`) call it for you - so there is rarely a reason for you to call it yourself. - - You may call this method _exactly once_. If you must change - this default in the middle of your test run, call `resetTestEnvironment` first. - - Specify the Angular compiler factory, a `PlatformRef`, and a default Angular testing module. - Alternatives for non-browser platforms are available in the general form - `@angular/platform-/testing/`. - -
- resetTestEnvironment - - - Reset the initial test environment, including the default testing module. - -
- -A few of the `TestBed` instance methods are not covered by static `TestBed` _class_ methods. -These are rarely needed. - -{@a component-fixture-api-summary} - -#### The _ComponentFixture_ - -The `TestBed.createComponent` -creates an instance of the component `T` -and returns a strongly typed `ComponentFixture` for that component. - -The `ComponentFixture` properties and methods provide access to the component, -its DOM representation, and aspects of its Angular environment. - -{@a component-fixture-properties} - -#### _ComponentFixture_ properties - -Here are the most important properties for testers, in order of likely utility. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Properties - - Description -
- componentInstance - - - The instance of the component class created by `TestBed.createComponent`. - -
- debugElement - - - The `DebugElement` associated with the root element of the component. - - The `debugElement` provides insight into the component and its DOM element during test and debugging. - It's a critical property for testers. The most interesting members are covered [below](#debug-element-details). - -
- nativeElement - - - The native DOM element at the root of the component. - -
- changeDetectorRef - - - The `ChangeDetectorRef` for the component. - - The `ChangeDetectorRef` is most valuable when testing a - component that has the `ChangeDetectionStrategy.OnPush` method - or the component's change detection is under your programmatic control. - -
- -{@a component-fixture-methods} - -#### _ComponentFixture_ methods - -The _fixture_ methods cause Angular to perform certain tasks on the component tree. -Call these method to trigger Angular behavior in response to simulated user action. - -Here are the most useful methods for testers. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Methods - - Description -
- detectChanges - - - Trigger a change detection cycle for the component. - - Call it to initialize the component (it calls `ngOnInit`) and after your - test code, change the component's data bound property values. - Angular can't see that you've changed `personComponent.name` and won't update the `name` - binding until you call `detectChanges`. - - Runs `checkNoChanges` afterwards to confirm that there are no circular updates unless - called as `detectChanges(false)`; - -
- autoDetectChanges - - - Set this to `true` when you want the fixture to detect changes automatically. - - When autodetect is `true`, the test fixture calls `detectChanges` immediately - after creating the component. Then it listens for pertinent zone events - and calls `detectChanges` accordingly. - When your test code modifies component property values directly, - you probably still have to call `fixture.detectChanges` to trigger data binding updates. - - The default is `false`. Testers who prefer fine control over test behavior - tend to keep it `false`. - -
- checkNoChanges - - - Do a change detection run to make sure there are no pending changes. - Throws an exceptions if there are. -
- isStable - - - If the fixture is currently _stable_, returns `true`. - If there are async tasks that have not completed, returns `false`. - -
- whenStable - - - Returns a promise that resolves when the fixture is stable. - - To resume testing after completion of asynchronous activity or - asynchronous change detection, hook that promise. - See [above](#when-stable). - -
- destroy - - - Trigger component destruction. - -
- -{@a debug-element-details} - -#### _DebugElement_ - -The `DebugElement` provides crucial insights into the component's DOM representation. - -From the test root component's `DebugElement` returned by `fixture.debugElement`, -you can walk (and query) the fixture's entire element and component subtrees. - -Here are the most useful `DebugElement` members for testers, in approximate order of utility: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Member - - Description -
- nativeElement - - - The corresponding DOM element in the browser (null for WebWorkers). - -
- query - - - Calling `query(predicate: Predicate)` returns the first `DebugElement` - that matches the [predicate](#query-predicate) at any depth in the subtree. - -
- queryAll - - - Calling `queryAll(predicate: Predicate)` returns all `DebugElements` - that matches the [predicate](#query-predicate) at any depth in subtree. - -
- injector - - - The host dependency injector. - For example, the root element's component instance injector. - -
- componentInstance - - - The element's own component instance, if it has one. - -
- context - - - An object that provides parent context for this element. - Often an ancestor component instance that governs this element. - - When an element is repeated within `*ngFor`, the context is an `NgForRow` whose `$implicit` - property is the value of the row instance value. - For example, the `hero` in `*ngFor="let hero of heroes"`. - -
- children - - - The immediate `DebugElement` children. Walk the tree by descending through `children`. - -
- - `DebugElement` also has `childNodes`, a list of `DebugNode` objects. - `DebugElement` derives from `DebugNode` objects and there are often - more nodes than elements. Testers can usually ignore plain nodes. - -
-
- parent - - - The `DebugElement` parent. Null if this is the root element. - -
- name - - - The element tag name, if it is an element. - -
- triggerEventHandler - - - Triggers the event by its name if there is a corresponding listener - in the element's `listeners` collection. - The second parameter is the _event object_ expected by the handler. - See [above](#trigger-event-handler). - - If the event lacks a listener or there's some other problem, - consider calling `nativeElement.dispatchEvent(eventObject)`. - -
- listeners - - - The callbacks attached to the component's `@Output` properties and/or the element's event properties. - -
- providerTokens - - - This component's injector lookup tokens. - Includes the component itself plus the tokens that the component lists in its `providers` metadata. - -
- source - - - Where to find this element in the source component template. - -
- references - - - Dictionary of objects associated with template local variables (e.g. `#foo`), - keyed by the local variable name. - -
- -{@a query-predicate} - -The `DebugElement.query(predicate)` and `DebugElement.queryAll(predicate)` methods take a -predicate that filters the source element's subtree for matching `DebugElement`. - -The predicate is any method that takes a `DebugElement` and returns a _truthy_ value. -The following example finds all `DebugElements` with a reference to a template local variable named "content": - - - -The Angular `By` class has three static methods for common predicates: - -- `By.all` - return all elements. -- `By.css(selector)` - return elements with matching CSS selectors. -- `By.directive(directive)` - return elements that Angular matched to an instance of the directive class. - - - -
- -{@a useful-tips} - -## Useful tips - -{@a q-spec-file-location} - -#### Place your spec file next to the file it tests - -It's a good idea to put unit test spec files in the same folder -as the application source code files that they test: - -- Such tests are easy to find. -- You see at a glance if a part of your application lacks tests. -- Nearby tests can reveal how a part works in context. -- When you move the source (inevitable), you remember to move the test. -- When you rename the source file (inevitable), you remember to rename the test file. - -{@a q-specs-in-test-folder} - -#### Place your spec files in a test folder - -Application integration specs can test the interactions of multiple parts -spread across folders and modules. -They don't really belong to any part in particular, so they don't have a -natural home next to any one file. - -It's often better to create an appropriate folder for them in the `tests` directory. - -Of course specs that test the test helpers belong in the `test` folder, -next to their corresponding helper files. - -{@a q-kiss} - -#### Keep it simple - -[Component class testing](#component-class-testing) should be kept very clean and simple. -It should test only a single unit. On a first glance, you should be able to understand -what the test is testing. If it's doing more, then it doesn't belong here. - -{@a q-end-to-end} - -#### Use E2E (end-to-end) to test more than a single unit - -E2E tests are great for high-level validation of the entire system. -But they can't give you the comprehensive test coverage that you'd expect from unit tests. - -E2E tests are difficult to write and perform poorly compared to unit tests. -They break easily, often due to changes or misbehavior far removed from the site of breakage. - -E2E tests can't easily reveal how your components behave when things go wrong, -such as missing or bad data, lost connectivity, and remote service failures. - -E2E tests for apps that update a database, -send an invoice, or charge a credit card require special tricks and back-doors to prevent -accidental corruption of remote resources. -It can even be hard to navigate to the component you want to test. - -Because of these many obstacles, you should test DOM interaction -with unit testing techniques as much as possible. diff --git a/aio/content/guide/upgrade-setup.md b/aio/content/guide/upgrade-setup.md index f45b5ab9f2..284076e42f 100644 --- a/aio/content/guide/upgrade-setup.md +++ b/aio/content/guide/upgrade-setup.md @@ -308,7 +308,7 @@ So when IE is refreshed (manually or automatically by `ng serve`), sometimes the ## Appendix: Test using `fakeAsync()/async()` -If you use the `fakeAsync()/async()` helper function to run unit tests (for details, read the [Testing guide](guide/testing#async-test-with-fakeasync)), you need to import `zone.js/dist/zone-testing` in your test setup file. +If you use the `fakeAsync()/async()` helper function to run unit tests (for details, read the [Testing guide](guide/testing-components-scenarios#fake-async)), you need to import `zone.js/dist/zone-testing` in your test setup file.
If you create project with `Angular/CLI`, it is already imported in `src/test.ts`. diff --git a/aio/content/navigation.json b/aio/content/navigation.json index 5c40dc968f..7e883dc73a 100644 --- a/aio/content/navigation.json +++ b/aio/content/navigation.json @@ -526,28 +526,74 @@ "url": "guide/angular-compiler-options", "title": "Angular Compiler Options", "tooltip": "Configuring AOT compilation." - }, - { + }, + { "url": "guide/aot-metadata-errors", "title": "AOT Metadata Errors", "tooltip": "Troubleshooting AOT compilation." - }, - { - "url": "guide/template-typecheck", - "title": "Template Type-checking", - "tooltip": "Template type-checking in Angular." - } - ] - }, + }, + { + "url": "guide/template-typecheck", + "title": "Template Type-checking", + "tooltip": "Template type-checking in Angular." + } + ] + }, { "url": "guide/build", "title": "Building & Serving", "tooltip": "Building and serving Angular apps." }, { - "url": "guide/testing", "title": "Testing", - "tooltip": "Techniques and practices for testing an Angular app." + "tooltip": "Testing your Angular apps.", + "children": [ + { + "url": "guide/testing", + "title": "Intro to Testing", + "tooltip": "Introduction to testing an Angular app." + }, + { + "url": "guide/testing-code-coverage", + "title": "Code Coverage", + "tooltip": "Determine how much of your code is tested." + }, + { + "url": "guide/testing-services", + "title": "Testing Services", + "tooltip": "How to test services." + }, + { + "url": "guide/testing-components-basics", + "title": "Basics of Testing Components", + "tooltip": "The fundamentals of how to test components." + }, + { + "url": "guide/testing-components-scenarios", + "title": "Component Testing Scenarios", + "tooltip": "Use cases for testing components." + }, + { + "url": "guide/testing-attribute-directives", + "title": "Testing Attribute Directives", + "tooltip": "How to test attribute directives." + }, + { + "url": "guide/testing-pipes", + "title": "Testing Pipes", + "tooltip": "Writing tests for pipes." + }, + { + "url": "guide/test-debugging", + "title": "Debugging Tests", + "tooltip": "How to debug tests." + }, + { + "url": "guide/testing-utility-apis", + "title": "Testing Utility APIs", + "tooltip": "Features of the Angular testing utilities." + } + ] }, { "url": "guide/deployment",