Tidy up your tests using component test harnesses (2/3)
Tags: angular, webdev, tutorial, testing
Published: 2021-11-30 17:43:24

---
title: Tidy up your tests using component test harnesses (2/3)
published: true
description: About the Angular CDK testing API, how to get test harnesses, and how to interact with components.
tags: angular,webdev,tutorial,testing
series: tidy-your-unit-tests-with-test-harnesses 
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ha02qavmwsbcoawjam1q.jpg
---

In the last post, we learned about test harnesses and the value of using Component test harnesses in our automated tests. Let's take a closer look at the `@angular/cdk/testing` API to understand how we work with component test harnesses.

Check out the first in this series :arrow_down:

{% link alisaduncan/tidy-up-your-tests-using-component-test-harnesses-13-359a %}

## Loading test harnesses for the environment

Test harnesses are available for e2e and unit test environments. Out of the box, the `@angular/cdk/testing` supports test harnesses in Protractor for e2e and Karma for unit tests. If you use other testing frameworks, you'll have to find an implementation of `TestBedHarnessEnvironment` for your framework of choice.

We'll use the Karma unit test environment for our code examples.

_This post assumes knowledge of [building a site using Angular](https://angular.io/start) and [writing unit tests using Karma](https://angular.io/guide/testing). The examples shown are a simplified version from the project GitHub repo._

{% github alisaduncan/component-harness-code no-readme %}

## The `HarnessLoader`

To initialize the environment and create a `HarnessLoader` for your test suite, you'll set it up as part of your `TestBed` setup by passing in the `ComponentFixture` instance.

```ts
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
 
describe('Tidy Test', () => {
  let fixture: ComponentFixture<TidyTestComponent>;
  let loader: HarnessLoader;
 
  beforeEach(() => {
    TestBed.configureTestingModule({...});
    fixture = TestBed.createComponent(TidyTestComponent);
    loader = TestbedHarnessEnvironment.loader(fixture);
  });
});
```

The above code creates a `HarnessLoader` compatible with elements contained inside the fixture's DOM root. If you need to test an element outside the fixture's DOM root, such as a dialog, you can create a `HarnessLoader` at the DOM root.

```ts
const rootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture);
```

## Get component test harnesses

Now that we have the `HarnessLoader`, we can get the component test harnesses we want to interact with. The `HarnessLoader` and component test harnesses return promises, so you'll use the `async/await` pattern.

You can get an individual test harness or all test harnesses.

**Individual test harness**
Get an individual component test harness by specifying the element type you're looking for using the `getHarness` method. The method returns a test harness for the first requested test harness element type it comes across or rejects the promise if none exists.

```ts
const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness);
```

**All test harnesses**
Get all component test harnesses of a type by using the `getAllHarnesses` method. In this example, the method returns an array of the requested test harness element type `MatButtonHarness`.

```ts
const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness);
```

You can also create child loaders for certain sections of your template so you can focus on getting test harnesses. Create a child loader by passing in a selector and then get either an individual test harness or all test harnesses

```ts
const childLoader: HarnessLoader = await loader.getChildLoader('.my-selector');
const 
```

## Applying filters

To specify which component test harness you want to get, you can add a filter. The filtering capability and what you can filter on are unique for each component test harness. The `MatButtonHarness` can filter by text to find all `MatButtonHarness` with the text "delete" like this.

```ts
const btns: MatButtonHarness[] = await loader.getAllHarnesses(MatButtonHarness.with({text: 'delete'}));
```

Most Material component harnesses have filters built-in. So you can get all checked or unchecked `MatCheckBoxHarness`, for example. You will have to read each component test harness documentation or inspect its API.

## The host element

All test harnesses have a `TestElement` host. The `TestElement` is like the `DebugElement`'s `nativeElement`. With the `TestElement`, you can hover, click, blur, access class list and dimensions, and more! Access the `TestElement` like this

```ts
const btn: MatButtonHarness = await loader.getHarness(MatButtonHarness);
const btnHost: TestElement = await btn.host();
await btn.hover();
```

## Interacting with the component

We covered the essential functions to get test harnesses and interact with the underlying host element. How about working with the component test harness itself, such as checking the checkbox or selecting a menu item? Functions like these are unique to each component, so you'll have to refer to the API for the component test harness. Angular Material components have documentation for the component test harness so that you can look at the API overview for the component you're interested in [on their site](https://material.angular.io/components).

## Optimizing test harness interactions

We've been using async/await for all the test harness interactions. We can optimize the interactions using a helper method, `parallel`, which parallelizes the async operations and optimizes for change detection! For example, you can get the checked status and the label for a checkbox in one go like this.

```ts
const checkbox: MatCheckboxHarness = await loader.getHarness(MatCheckboxHarness);
const [checked, label] = await parallel(() => [
  checkbox.isChecked(),
  checkbox.getLabelText()
]);
```

## Expanding beyond Angular Material Components

We learned about using the `HarnessLoader` to get test harnesses and ways we can interact with them. But what if you don't work with Material? Or what if you have custom UI components in your app? You can use the `@angular/cdk/testing` API to create custom component harnesses. We'll talk about how to do that in the next post in the series.

Have you used Angular Material component test harnesses? Let me know in the comments below if you have and what your thoughts are.