I've got this simple component.
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-spinner',
template: `
<ng-container *ngIf="loading; else valueTpl">
<strong>loading....</strong>
</ng-container>
<ng-template #valueTpl>
<span>{{ value }}</span>
</ng-template>
`
})
export class SpinnerComponent {
@Input() loading = false;
@Input() value!: string;
}
in the running up it works nice but when I go to test the test fails
it('should show spinner when loading true', () => {
component.loading = true;
fixture.detectChanges();
const strong = debugElement.query(By.css('strong'));
const el: HTMLElement = strong.nativeElement;
expect(el).not.toBeNull();
});
Whats the right way to test component with ChangeDetectionStrategy.OnPush?
Waiting in a better solution I worked it out with:
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SpinnerComponent]
})
.overrideComponent(SpinnerComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
})
.compileComponents();
}));
Input Reference But with OnPush strategy, the change detector is only triggered if the data passed on @Input() has a new reference. This is why using immutable objects is preferred, because immutable objects can be modified only by creating a new object reference.
OnPush means that the change detector's mode will be set to CheckOnce during hydration. Default means that the change detector's mode will be set to CheckAlways during hydration.
What is Change Detection Strategy in Angular ? Angular Change Detection Strategy are the methods by which the updates to the component is tracked and component is triggered to Re-render.
Syntax. ng test run the unit test cases on angular app code.
What works is to override component's changeDetection to default AND provide ComponentFixtureAutoDetect as true.
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SpinnerComponent],
providers: [
{provide: ComponentFixtureAutoDetect, useValue: true}
]
})
.overrideComponent(SpinnerComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
})
.compileComponents();
fixture = TestBed.createComponent(SpinnerComponent);
component = fixture.componentInstance;
}));
Now fixture.detectChanges()
should work in the tests.
The best way is to provide TestingHostComponent that wraps a component you want to test:
@Component({
template: `<app-spinner [loading]="loading"></app-spinner>`
})
class TestHostComponent {
@ViewChild(SpinnerComponent, { static: true }) spinnerComponent?: SpinnerComponent;
loading?: boolean;
}
I've tried many ways (including @Tschonner's answer) but none of them worked well. Finally, I got this working with the followings, using runOnPushChangeDetection(fixture) instead of fixture.detectChanges():
export async function runOnPushChangeDetection<T>(cf: ComponentFixture<T>) {
const cd = cf.debugElement.injector.get<ChangeDetectorRef>(
// tslint:disable-next-line:no-any
ChangeDetectorRef as any
);
cd.markForCheck();
cd.detectChanges();
await cf.whenStable();
return;
}
...
beforeEach(async() => {
await TestBed.configureTestingModule({
imports: [
IonicModule.forRoot()
],
declarations: [ LoaderComponent ],
})
.overrideComponent(LoaderComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
})
.compileComponents();
fixture = TestBed.createComponent(LoaderComponent);
component = fixture.componentInstance;
});
it('should ...', async() => {
component.prop = true;
await runOnPushChangeDetection(fixture);
const debugElement: DebugElement = fixture.debugElement;
const compiled = debugElement.nativeElement;
const img = debugElement.query(By.css('img'));
expect(img).toBeDefined();
...
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With