Skip to content

Commit 790221b

Browse files
amysortokirjs
authored andcommitted
docs: add component harnesses guides (angular#59078)
PR Close angular#59078
1 parent fdbb57f commit 790221b

File tree

5 files changed

+592
-0
lines changed

5 files changed

+592
-0
lines changed

adev/src/app/sub-navigation-data.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,26 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
499499
path: 'guide/testing/utility-apis',
500500
contentPath: 'guide/testing/utility-apis',
501501
},
502+
{
503+
label: 'Component harnesses overview',
504+
path: 'guide/testing/component-harnesses-overview',
505+
contentPath: 'guide/testing/component-harnesses-overview',
506+
},
507+
{
508+
label: 'Using component harnesses in tests',
509+
path: 'guide/testing/using-component-harnesses',
510+
contentPath: 'guide/testing/using-component-harnesses',
511+
},
512+
{
513+
label: 'Creating harnesses for your components',
514+
path: 'guide/testing/creating-component-harnesses',
515+
contentPath: 'guide/testing/creating-component-harnesses',
516+
},
517+
{
518+
label: 'Adding harness support for additional testing environments',
519+
path: 'guide/testing/component-harnesses-testing-environments',
520+
contentPath: 'guide/testing/component-harnesses-testing-environments',
521+
},
502522
],
503523
},
504524
{
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Component harnesses overview
2+
3+
A <strong>component harness</strong> is a class that allows tests to interact with components the way an end user does via a supported API. You can create test harnesses for any component, ranging from small reusable widgets to full pages.
4+
5+
Harnesses offer several benefits:
6+
- They make tests less brittle by insulating themselves against implementation details of a component, such as its DOM structure
7+
- They make tests become more readable and easier to maintain
8+
- They can be used across multiple testing environments
9+
10+
<docs-code language="typescript">
11+
// Example of test with a harness for a component called MyButtonComponent
12+
it('should load button with exact text', async () => {
13+
const button = await loader.getHarness(MyButtonComponentHarness);
14+
expect(await button.getText()).toBe('Confirm');
15+
});
16+
</docs-code>
17+
18+
Component harnesses are especially useful for shared UI widgets. Developers often write tests that depend on private implementation details of widgets, such as DOM structure and CSS classes. Those dependencies make tests brittle and hard to maintain. Harnesses offer an alternative— a supported API that interacts with the widget the same way an end-user does. Widget implementation changes now become less likely to break user tests. For example, [Angular Material](https://material.angular.io/components/categories) provides a test harness for each component in the library.
19+
20+
Component harnesses support multiple testing environments. You can use the same harness implementation in both unit and end-to-end tests. Test authors only need to learn one API and component authors don't have to maintain separate unit and end-to-end test implementations.
21+
22+
Many developers can be categorized by one of the following developer type categories: test authors, component harness authors, and harness environment authors. Use the table below to find the most relevant section in this guide based on these categories:
23+
24+
| Developer Type | Description | Relevant Section |
25+
|:--- | :--- | :--- |
26+
| Test Authors | Developers that use component harnesses written by someone else to test their application. For example, this could be an app developer who uses a third-party menu component and needs to interact with the menu in a unit test. | [Using component harnesses in tests](guide/testing/using-component-harnesses) |
27+
| Component harness authors | Developers who maintain some reusable Angular components and want to create a test harness for its users to use in their tests. For example, an author of a third party Angular component library or a developer who maintains a set of common components for a large Angular application. | [Creating component harnesses for your components](guide/testing/creating-component-harnesses ) |
28+
| Harness environment authors | Developers who want to add support for using component harnesses in additional testing environments. For information on supported testing environments out-of-the-box, see the [test harness environments and loaders](guide/testing/using-component-harnesses#test-harness-environments-and-loaders). | [Adding support for additional testing environments](guide/testing/component-harnesses-testing-environments) |
29+
30+
For the full API reference, please see the [Angular CDK's component harness API reference page](https://material.angular.io/cdk/test-harnesses/api).
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Adding harness support for additional testing environments
2+
3+
## Before you start
4+
5+
Tip: This guide assumes you've already read the [component harnesses overview guide](guide/testing/component-harnesses-overview). Read that first if you're new to using component harnesses.
6+
7+
### When does adding support for a test environment make sense?
8+
9+
To use component harnesses in the following environments, you can use Angular CDK's two built-in environments:
10+
- Unit tests
11+
- WebDriver end-to-end tests
12+
13+
To use a supported testing environment, read the [Creating harnesses for your components guide](guide/testing/creating-component-harnesses).
14+
15+
Otherwise, to add support for other environments, you need to define how to interact with a DOM element and how DOM interactions work in your environment. Continue reading to learn more.
16+
17+
### CDK Installation
18+
19+
The [Component Dev Kit (CDK)](https://material.angular.io/cdk/categories) is a set of behavior primitives for building components. To use the component harnesses, first install `@angular/cdk` from npm. You can do this from your terminal using the Angular CLI:
20+
21+
<docs-code language="shell">
22+
ng add @angular/cdk
23+
</docs-code>
24+
25+
## Creating a `TestElement` implementation
26+
27+
Every test environment must define a `TestElement` implementation. The `TestElement` interface serves as an environment-agnostic representation of a DOM element. It enables harnesses to interact with DOM elements regardless of the underlying environment. Because some environments don't support interacting with DOM elements synchronously (e.g. WebDriver), all `TestElement` methods are asynchronous, returning a `Promise` with the result of the operation.
28+
29+
`TestElement` offers a number of methods to interact with the underlying DOM such as `blur()`, `click()`, `getAttribute()`, and more. See the [TestElement API reference page](https://material.angular.io/cdk/test-harnesses/api#TestElement) for the full list of methods.
30+
31+
The `TestElement` interface consists largely of methods that resemble methods available on `HTMLElement`. Similar methods exist in most test environments, which makes implementing the methods fairly straightforward. However, one important difference to note when implementing the `sendKeys` method, is that the key codes in the `TestKey` enum likely differ from the key codes used in the test environment. Environment authors should maintain a mapping from `TestKey` codes to the codes used in the particular testing environment.
32+
33+
The [UnitTestElement](https://github.com/angular/components/blob/main/src/cdk/testing/testbed/unit-test-element.ts#L33) and [SeleniumWebDriverElement](https://github.com/angular/components/blob/main/src/cdk/testing/selenium-webdriver/selenium-webdriver-keys.ts#L16) implementations in Angular CDK serve as good examples of implementations of this interface.
34+
35+
## Creating a `HarnessEnvironment` implementation
36+
Test authors use `HarnessEnvironment` to create component harness instances for use in tests. `HarnessEnvironment` is an abstract class that must be extended to create a concrete subclass for the new environment. When supporting a new test environment, create a `HarnessEnvironment` subclass that adds concrete implementations for all abstract members.
37+
38+
`HarnessEnvironment` has a generic type parameter: `HarnessEnvironment<E>`. This parameter, `E`, represents the raw element type of the environment. For example, this parameter is Element for unit test environments.
39+
40+
The following are the abstract methods that must be implemented:
41+
42+
| Method | Description |
43+
|:--- | :--- |
44+
| `abstract getDocumentRoot(): E` | Gets the root element for the environment (e.g. `document.body`). |
45+
| `abstract createTestElement(element: E): TestElement` | Creates a `TestElement` for the given raw element. |
46+
| `abstract createEnvironment(element: E): HarnessEnvironment` | Creates a `HarnessEnvironment` rooted at the given raw element. |
47+
| `abstract getAllRawElements(selector: string): Promise<E[]>` | Gets all of the raw elements under the root element of the environment matching the given selector. |
48+
| `abstract forceStabilize(): Promise<void>` | Gets a `Promise` that resolves when the `NgZone` is stable. Additionally, if applicable, tells `NgZone` to stabilize (e.g. calling `flush()` in a `fakeAsync` test). |
49+
| `abstract waitForTasksOutsideAngular(): Promise<void>` | Gets a `Promise` that resolves when the parent zone of `NgZone` is stable. |
50+
51+
In addition to implementing the missing methods, this class should provide a way for test authors to get `ComponentHarness` instances. You should define a protected constructor and provide a static method called `loader` that returns a `HarnessLoader` instance. This allows test authors to write code like: `SomeHarnessEnvironment.loader().getHarness(...)`. Depending on the needs of the particular environment, the class may provide several different static methods or require arguments to be passed. (e.g. the `loader` method on `TestbedHarnessEnvironment` takes a `ComponentFixture`, and the class provides additional static methods called `documentRootLoader` and `harnessForFixture`).
52+
53+
The [`TestbedHarnessEnvironment`](https://github.com/angular/components/blob/main/src/cdk/testing/testbed/testbed-harness-environment.ts#L89) and [SeleniumWebDriverHarnessEnvironment](https://github.com/angular/components/blob/main/src/cdk/testing/selenium-webdriver/selenium-web-driver-harness-environment.ts#L71) implementations in Angular CDK serve as good examples of implementations of this interface.
54+
55+
## Handling auto change detection
56+
In order to support the `manualChangeDetection` and parallel APIs, your environment should install a handler for the auto change detection status.
57+
58+
When your environment wants to start handling the auto change detection status it can call `handleAutoChangeDetectionStatus(handler)`. The handler function will receive a `AutoChangeDetectionStatus` which has two properties `isDisabled` and `onDetectChangesNow()`. See the [AutoChangeDetectionStatus API reference page](https://material.angular.io/cdk/test-harnesses/api#AutoChangeDetectionStatus) for more information.
59+
If your environment wants to stop handling auto change detection status it can call `stopHandlingAutoChangeDetectionStatus()`.

0 commit comments

Comments
 (0)