Angular 2 unit testing - @ViewChild is undefined

Unit TestingAngularAngular2 TemplateNg2 Bootstrap

Unit Testing Problem Overview


I am writing an Angular 2 unit test. I have a @ViewChild subcomponent that I need to recognize after the component initializes. In this case it's a Timepicker component from the ng2-bootstrap library, though the specifics shouldn't matter. After I detectChanges() the subcomponent instance is still undefined.

Pseudo-code:

@Component({
    template: `
        <form>
            <timepicker
                #timepickerChild
                [(ngModel)]="myDate">
            </timepicker>
        </form>
    `
})
export class ExampleComponent implements OnInit {
    @ViewChild('timepickerChild') timepickerChild: TimepickerComponent;
    public myDate = new Date();
}
    
// Spec
describe('Example Test', () => {
    let exampleComponent: ExampleComponent;
    let fixture: ComponentFixture<ExampleComponent>;

    beforeEach(() => {
        TestBed.configureTestingModel({
            // ... whatever needs to be configured
        });
        fixture = TestBed.createComponent(ExampleComponent);
    });

    it('should recognize a timepicker'. async(() => {
        fixture.detectChanges();
        const timepickerChild: Timepicker = fixture.componentInstance.timepickerChild;
        console.log('timepickerChild', timepickerChild)
    }));
});

The pseudo-code works as expected until you reach the console log. The timepickerChild is undefined. Why is this happening?

Unit Testing Solutions


Solution 1 - Unit Testing

I think it should work. Maybe you forgot to import some module in your configuration. Here is the complete code for test:

import { TestBed, ComponentFixture, async } from '@angular/core/testing';

import { Component, DebugElement } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { ExampleComponent } from './test.component';
import { TimepickerModule, TimepickerComponent } from 'ng2-bootstrap/ng2-bootstrap';

describe('Example Test', () => {
  let exampleComponent: ExampleComponent;
  let fixture: ComponentFixture<ExampleComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [FormsModule, TimepickerModule.forRoot()],
      declarations: [
        ExampleComponent
      ]
    });
    fixture = TestBed.createComponent(ExampleComponent);
  });

  it('should recognize a timepicker', async(() => {
    fixture.detectChanges();
    const timepickerChild: TimepickerComponent = fixture.componentInstance.timepickerChild;
    console.log('timepickerChild', timepickerChild);
    expect(timepickerChild).toBeDefined();
  }));
});

Plunker Example

Solution 2 - Unit Testing

Make sure your child component does not have a *ngIf that is evaluating to false. If so, it will result in the child component as being undefined.

Solution 3 - Unit Testing

In most cases just add it to declaration and you are good to go.

beforeEach(async(() => {
        TestBed
            .configureTestingModule({
                imports: [],
                declarations: [TimepickerComponent],
                providers: [],
            })
            .compileComponents() 

Solution 4 - Unit Testing

If you want to test the main component with a stub child component, you need to add a provider to the stub child component; as explained in the article Angular Unit Testing @ViewChild.

import { Component } from '@angular/core';
import { ChildComponent } from './child.component';

@Component({
  selector: 'app-child',
  template: '',
  providers: [
    {
      provide: ChildComponent,
      useClass: ChildStubComponent
    }
  ]
})
export class ChildStubComponent {
  updateTimeStamp() {}
}

Note the providers metadata, to use the class ChildStubComponent when ChildComponent is required.

You can then create your parent component normally, its child will be created with the type ChildStubComponent.

Solution 5 - Unit Testing

For an alternative way of doing this refer this post:

https://stackoverflow.com/a/70966565/11797105

Solution 6 - Unit Testing

Even after following everything from the accepted answer, you are getting undefined instance of child component then kindly check if that component is visible.

In my case, there was *ngIf applied on the control that's why instance of child was undefined then I removed and checked and it worked for me

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionebakuninView Question on Stackoverflow
Solution 1 - Unit TestingyurzuiView Answer on Stackoverflow
Solution 2 - Unit TestingZobair SaleemView Answer on Stackoverflow
Solution 3 - Unit Testingomri.sView Answer on Stackoverflow
Solution 4 - Unit Testingarthur.swView Answer on Stackoverflow
Solution 5 - Unit TestingLutherView Answer on Stackoverflow
Solution 6 - Unit TestingMohini MhetreView Answer on Stackoverflow