Angular-Testing-Library
John Peters
Posted on February 20, 2020
The Angular-Testing-Library implementation fits into the Karma/Jasmine framework like this:
async function renderCheckbox(checked) {
let temp = await render(CheckboxComponent, {
imports: [ReactiveFormsModule]
});
let control = {
GroupName: "This group name test",
PropertyName: "The Checkbox component PropertyName Test",
Type: "checkbox",
CurrentValue: "checked",
Checked: checked
};
temp.fixture.componentInstance.control = control;
temp.fixture.detectChanges();
return { temp, control };
}
The key function is "render" from which is an import from this framework.
Assume an Angular Component like this:
Typescript
export class CheckboxComponent implements OnInit {
@Input("control") control = {
GroupName: "",
PropertyName: "",
Type: "",
CurrentValue: "",
Checked: ""
};
@Output() emitChange: EventEmitter<any> = new EventEmitter();
public myFormGroup: FormGroup = new FormGroup({
checkbox: new FormControl(this.control, [])
});
constructor() {}
ngOnInit() {}
onCheckBoxChanged() {
let context = this.myFormGroup.controls.checkbox.value;
let change = {
GroupName: this.control.GroupName,
PropertyName: this.control.PropertyName,
Type: this.control.Type,
CurrentValue: context
};
this.emitChange.emit(change);
}
}
HTML
<form [formGroup]="myFormGroup">
<label>{{ control.PropertyName }}</label>
<input
data-testid="checkbox"
*ngIf="control.Type === 'checkbox'"
formControlName="checkbox"
type="checkbox"
[(ngModel)]="control.Checked"
(change)="onCheckBoxChanged()"
/>
</form>
Write your Angular-Testing-Library Test
This construct bypasses the default Angular test schematic. It majorly improves the dependency chain manual configuration problem.
import { render} from "@testing-library/angular";
import { ReactiveFormsModule } from "@angular/forms";
import { CheckboxComponent } from "../checkbox/checkbox.component";
//our wrapper for the render function
async function renderCheckbox(checked) {
let temp =
await render(CheckboxComponent, {
imports: [ReactiveFormsModule]
});
let control = {
GroupName: "This group name test",
PropertyName: "The Checkbox component PropertyName Test",
Type: "checkbox",
CurrentValue: "checked",
Checked: checked
};
temp.fixture.componentInstance.control = control;
temp.fixture.detectChanges();
return { temp, control };
}
//jasmine tests start here
describe("CheckboxComponent", () => {
it("should have a PropertyName", async () => {
let { temp, control } =
await renderCheckbox(true);
let label = await temp.findByText(control.PropertyName);
expect(label.innerText).toContain(control.PropertyName);
});
it("should not be checked", async () => {
let { temp, control } =
await renderCheckbox(false);
let cb: any = temp.getByTestId("checkbox");
expect(cb.checked).toBe(false);
});
it("should be checked", async () => {
let { temp, control } =
await renderCheckbox(true);
let cb: any = temp.getByTestId("checkbox");
expect(cb.checked).toBe(true);
});
it("should emit change", async () => {
let { temp, control } =
await renderCheckbox(true);
temp.fixture.componentInstance.emitChange.subscribe(change => {
expect(change.CurrentValue).toBe(false);
});
let checkbox: any = temp.getAllByTestId("checkbox")[0];
checkbox.click();
let actual = checkbox.checked;
let currentContext =
temp.fixture.componentInstance.myFormGroup.controls.checkbox.value;
expect(actual).toBe(false);
});
});
Notes
This function is the key:
async function renderCheckbox(checked) {
let temp = await render(CheckboxComponent, {
imports: [ReactiveFormsModule]
});
let control = {
GroupName: "This group name test",
PropertyName: "The Checkbox component PropertyName Test",
Type: "checkbox",
CurrentValue: "checked",
Checked: checked
};
temp.fixture.componentInstance.control = control;
temp.fixture.detectChanges();
return { temp, control };
}
Notice that temp is the RenderResult, See this article for what a RenderResult looks liks:
Converting native Karma, Jasmine tests to use Angular-Testing-Library
John Peters ・ Feb 19 '20
The renderResult.fixture.componentInstance is the Angular Component. Getting addressibility to any HTMLElement in the component is easily done using the attribute as shown in the HTML above; (named data-testid) as shown here:
data-testid="checkbox"
Allowing this statement to get the checkbox. We ensure it's type of any because it allows us to easily test the "checked" property.
let cb: any = temp.getByTestId("checkbox");
expect(cb.checked).toBe(false);
Note that we can subscribe to the component output as follows:
temp.fixture.componentInstance.emitChange.subscribe(change => {
(change.CurrentValue).toBe(false);
});
However this subscription only works when we emulate a click event on the checkbox, like this:
let checkbox: any = temp.getAllByTestId("checkbox")[0];
checkbox.click();
All in all a nice way to allow something else to do the rendering without worrying about the dependencies. Angular-Testing-Library is a good tool.
JWP2020
Posted on February 20, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.