🤔
Tests UI interactivity within an Angular application using an unit testing library, but concepts apply to all forms of testing
A test harness encapsulates implementation details of HTML fragments into an API for the purposes of testing
All Angular Material components have test harnesses starting in v12
The new MDC changes in v15 means your old tests might need updates
it('no test harness', async () => {
});
it('no test harness', async () => {
const input = fixture.debugElement.query(By.css('input'));
expect(input).not.toBeNull();
input.nativeElement.value = 'email@email.email';
input.nativeElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
await fixture.whenStable();
});
it('yay test harness!', async () => {
});
it('yay test harness!', async () => {
const emailInputEl = await loader.getHarness(MatInputHarness);
await emailInputEl.setValue('email@email.email');
});
it('interact with slide toggle', () => {
const slideToggleEl
= fixture.debugElement.query(By.css('mat-slide-toggle'));
});
it('interact with slide toggle', () => {
const slideToggleEl
= fixture.debugElement.query(By.css('mat-slide-toggle'));
const slideToggleBtn
= slideToggleEl.nativeElement.querySelector('button');
});
it('interact with slide toggle', () => {
const slideToggleEl
= fixture.debugElement.query(By.css('mat-slide-toggle'));
const slideToggleBtn
= slideToggleEl.nativeElement.querySelector('button');
const slideToggleCheck
= slideToggleEl.nativeElement.querySelector('input[type="checkbox"]');
});
it('interact with slide toggle test harness', async () => {
const slideToggleHarness
= await loader.getHarness(MatSlideToggleHarness);
await slideToggleHarness.toggle();
});
The Angular CDK testing library supports testing interactions with components
TestBed
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
describe('KDrama Component Test Harness', () => {
let loader: HarnessLoader;
beforeEach(() => {
TestBed.configureTestingModule({...});
fixture = TestBed.createComponent(MyKDramaComponent);
loader = TestbedHarnessEnvironment.loader(fixture);
});
});
TestbedHarnessEnvironment
Supports unit testing using Karma
HarnessLoader
Source from cdk/testing/component-harness.ts
const input: MatInputHarness
= await loader.getHarness(MatInputHarness);
const input: MatInputHarness|null
= await loader.getHarnessOrNull(MatInputHarness);
const inputs: MatInputHarness[]
= await loader.getAllHarnesses(MatInputHarness);
const childLoader: HarnessLoader
= await loader.getChildLoader('.my-selector');
const childInput: MatInputHarness
= await childLoader.getHarness(MatInputHarness);
loader.getHarness(MatInputHarness.with({options}));
Your options depend on the harness you are using
InputHarnessFilters
interface
Source from material/input/testing/input-harness-filters.ts
MatInputHarness
by value
const inputs: MatInputHarness[] = await loader.getAllHarnesses(
MatInputHarness.with({
value: 'only cool comments, please!'
})
);
Once you have a harness, it is up to the API of that harness on interactions you can take
Check out the documentation!
Not quite accurate since MatInput
extends from
MatFormFieldControlHarness
, but simplifying for this presentation
ComponentHarness
base class
Source from cdk/testing/component-harness.ts
TestElement
Source from cdk/testing/test-element.ts
Notice everything is async
async
?parallel
that we can use
const input = await loader.getHarness(MatInputHarness);
const [name, value] = await parallel(() => [
input.getName(),
input.getValue()
]);
You're now winning at writing tests with Material component test harnesses! Let's win some more!
Write custom component test harnesses for
Maximum Winning!
import { BaseHarnessFilters } from '@angular/cdk/testing';
export interface AddCommentHarnessFilters extends BaseHarnessFilters {
}
import { BaseHarnessFilters } from '@angular/cdk/testing';
export interface AddCommentHarnessFilters extends BaseHarnessFilters {
comment?: string;
}
import { ComponentHarness } from '@angular/cdk/testing';
export class AddCommentHarness extends ComponentHarness {
}
import { ComponentHarness } from '@angular/cdk/testing';
export class AddCommentHarness extends ComponentHarness {
static hostSelector = 'app-add-comment';
}
ComponentHarness
locator methods scale with your component complexity
When writing components, small is winning
Code sample
github.com/alisaduncan/component-harness-ftw-code
Slides componentharnessesftw.alisaduncan.dev
Test harnesses posts bit.ly/ADUnitTestingPost