57 votes

Tests unitaires d'Angular 2 - @ViewChild est indéfini

Je suis en train d'écrire un test unitaire Angular 2. J'ai un @ViewChild que je dois reconnaître après l'initialisation du composant. Dans ce cas, il s'agit d'un Timepicker de la bibliothèque ng2-bootstrap, mais les spécificités ne devraient pas avoir d'importance. Après avoir detectChanges() l'instance du sous-composant n'est toujours pas définie.

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)
    }));
});

Le pseudo-code fonctionne comme prévu jusqu'à ce que vous atteigniez le journal de la console. Le pseudo-code fonctionne comme prévu jusqu'à ce que vous atteigniez le journal de la console. timepickerChild n'est pas défini. Pourquoi cela se produit-il ?

4 votes

Avez-vous trouvé une réponse ? J'ai le même problème.

3 votes

J'ai le vague sentiment que la plupart des votants ont un problème différent. Assurez-vous que votre composant enfant n'est pas caché par un composant *ngIf="false" directive. De même, après avoir fixé la condition de rendu à true , faire un fixture.detectChanges() qui (re)créera le composant enfant précédemment non défini.

37voto

yurzui Points 85802

Je pense que cela devrait fonctionner. Peut-être avez-vous oublié d'importer un module dans votre configuration. Voici le code complet pour le 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();
  }));
});

Exemple d'un plumitif

0 votes

Si je veux tester le composant principal sans inclure les enfants réels, existe-t-il un moyen de les simuler ?

0 votes

@Eugene Pouvez-vous fournir un exemple dans stackblitz ?

0 votes

Mais @ViewChild('timepickerChild') timepickerChild: TimepickerComponent; devrait être un champ privé.

13voto

Zobair Saleem Points 1

Assurez-vous que votre composant enfant n'a pas de *ngIf qui s'évalue à false. Si c'est le cas, le composant enfant sera indéfini.

0 votes

Très bonne prise, merci

0 votes

Merci beaucoup. J'ai résolu un problème grâce à votre réponse.

5voto

omri.s Points 25

Dans la plupart des cas, il suffit de l'ajouter à la déclaration et le tour est joué.

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

0 votes

Ce n'est pas une bonne solution à mon avis. Si vous déclarez des composants dans vos tests, vous avez une dépendance forte envers eux. Cela signifie que s'ils cessent de fonctionner (pour une raison ou une autre), votre test échouera également. Vous devriez plutôt opter pour des composants mocking (comme par exemple ici : medium.com/@cnunciato/ )

4voto

arthur.sw Points 5016

Si vous souhaitez tester le composant principal à l'aide d'un talon vous devez ajouter un fournisseur au composant enfant stub ; comme expliqué dans l'article Test unitaire d'Angular @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() {}
}

Notez que les fournisseurs pour utiliser la classe Composant ChildStub cuando Composant enfant est nécessaire.

Vous pouvez alors créer votre composant parent normalement, son enfant sera créé avec le type Composant ChildStub .

-1voto

Mohini Mhetre Points 342

Même après avoir suivi toutes les instructions de la réponse acceptée, vous obtenez une instance non définie du composant enfant, alors vérifiez si ce composant est visible.

Dans mon cas, il y avait *ngIf appliqué sur le contrôle, c'est pourquoi l'instance de l'enfant était indéfinie, puis j'ai supprimé et vérifié et cela a fonctionné pour moi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X