I have an Angular standalone component that has a child standalone component. In my unit test I want to mock said child component. When I check if the mocked component is rendered via fixture.debugElement, I see that the mocked component is used. But when I try to reference the ViewChild it is always undefined.
Any thoughts?
Angular Version: 17.1.0
Here's the relevant code:
child.component.ts
@Component({
selector: 'child',
standalone: true,
template: 'Child',
})
export class ChildComponent {}
app.component.ts
@Component({
selector: 'app-root',
standalone: true,
imports: [ChildComponent],
template: '<child></child>'
})
export class AppComponent implements AfterViewInit {
@ViewChild(ChildComponent) child!: ChildComponent;
ngAfterViewInit(): void {
// On serving the app this.child is defined
// In the tests this.child is undefined
console.log(this.child);
}
}
app.component.spec.ts
Mocked Child Component
@Component({
selector: 'child',
template: `Mocked child`,
standalone: true,
})
class MockChildComponent {}
Setup
beforeEach(waitForAsync(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent]
})
.overrideComponent(AppComponent, {
set: {
imports: [MockChildComponent]
}
})
.compileComponents();
fixture = TestBed.createComponent(AppComponent);
appComponent = fixture.componentInstance;
fixture.detectChanges();
}));
When I remove overrideComponent I do get a defined reference to the ViewChild. But then it is not mocked of course.
Tests
// OK
it('should have the rendered mocked child element', () => {
expect(
fixture.debugElement.query(By.css('child')).nativeElement.innerHTML,
).toContain('Mocked child');
});
// Fail: Expected undefined to be truthy.
it('should have the mocked child component', () => {
expect(appComponent.child).toBeTruthy();
});
Solved
Of course I fixed it just after posting this. I am my own rubber duck.. I'm posting the solution in case someone else runs into this issue.
To get a correct reference I changed the template in app.component.ts to
<child #child></child>:And changed the ViewChild to reference the temple ate variable instead of the component.
Now it works as expected.
Edit: moved the solution to an answer post instead of editing it in the question.