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
Top comments (1)
Thanks for writing about Angular Testing Library! π€