web-dev-qa-db-fra.com

Angular 4: Mock ElementRef

J'essaie de comprendre comment se moquer d'une ElementRef qui est injectée dans un composant. Mon composant est le suivant:

app.component.ts:

import { Component, ElementRef } from '@angular/core';

import { AppService } from './app.service';

@Component({
  selector: 'app-root',
  templateUrl: './app/app.component.html',
  styleUrls: ['./app/app.component.css']
})
export class AppComponent {
  title = 'app works!';

  constructor(private _elementRef: ElementRef, private _appService: AppService) {
    console.log(this._elementRef);
    console.log(this._appService);
  }
}

et mes spécifications de test comme suit:

app.component.spec.ts:

import { TestBed, async } from '@angular/core/testing';
import { ElementRef, Injectable } from '@angular/core';
import { AppComponent } from './app.component';
import { AppService } from './app.service';

@Injectable()
export class MockElementRef {
  nativeElement: {}  
}

@Injectable()
export class MockAppService {

}

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
      providers: [
        {provide: ElementRef, useClass: MockElementRef},
        {provide: AppService, useClass: MockAppService}
      ]
    }).compileComponents();
  }));

  ...
});

Lorsque les tests sont exécutés, la sortie du console.log dans le constructeur de app.component.ts est la suivante:

 console output

Comme vous pouvez le constater, cela consiste à injecter la MockAppService mais pas la MockElementRef (même s’ils se moquent tous les deux de la même manière).

Ce SO post suggère de le configurer comme vous le feriez avec d’autres simulacres, mais j’ai remarqué que c’était pour Angular 2 - alors je me demande si les choses ont changé dans Angular 4?

Vous pouvez trouver un Plunker avec le code ci-dessus et les tests Jasmine ici . Exécutez le Plunker puis cliquez sur le lien "Exécuter les tests unitaires" pour lancer les tests unitaires. La sortie de la console peut être observée dans les outils de développement/Firebug.

9
Ian A

La réponse courte - c'est par conception :)

Examinons étape par étape la réponse plus longue et essayons de comprendre ce qui se passe sous le capot lorsque nous configurons un module de test via TestBed.

Étape 1

Selon le code source de test_bed.ts :

configureTestingModule(moduleDef: TestModuleMetadata): void {
  if (moduleDef.providers) {
    this._providers.Push(...moduleDef.providers);
  }
  if (moduleDef.declarations) {
    this._declarations.Push(...moduleDef.declarations);
  }
  // ...
}

Comme nous pouvons le constater - la méthode configureTestingModule pousse simplement les instances fournies dans le tableau this._providers. Et ensuite, nous pouvons dire: hé, TestBed, donnez-moi ce fournisseur ElementRef:

  // ...
  let elRef: ElementRef;

  beforeEach(() => {
    TestBed.configureTestingModule({
      // ...
      providers: [{provide: ElementRef, useValue: new MockElementRef()}]
    });

    // ...
    elRef = TestBed.get(ElementRef);
  });

  it('test', () => {
    console.log(elRef);
  });

Dans la console, nous verrons:

enter image description here

La première console était consignée depuis le constructeur du composant et la seconde - depuis le test.Il semble donc que nous ayons affaire à deux instances différentes de ElementRef. Allons-nous en.

Étape 2

Jetons un coup d'œil à un autre exemple et disons que nous avons un composant qui injecte ElementRef et un autre service personnalisé AppService que nous avons créé auparavant:

export class HelloComponent  {
  constructor(private _elementRef: ElementRef, private _appService: AppService) {
    console.log(this._elementRef);
    console.log(this._appService);
  }
}

Lorsque nous testons ce composant - nous devons fournir AppService (le service lui-même ou sa maquette), MAIS, si nous ne fournissons pas ElementRef à TestBed - le test ne s'en plaindra jamais: NullInjectorError: No provider for ElementRef!.

Nous pouvons donc proposer que ElementRef ne ressemble pas à une dépendance et soit toujours lié au composant lui-même. Nous nous rapprochons de la réponse. :)

Étape 3

Examinons de plus près comment TestBed crée le composant: TestBed.createComponent(AppComponent). Ceci est une version très simplifiée du code source:

createComponent<T>(component: Type<T>): ComponentFixture<T> {
    this._initIfNeeded();
    const componentFactory = this._compiler.getComponentFactory(component);
    // ...
      const componentRef =
          componentFactory.create(Injector.NULL, [], `#${rootElId}`, this._moduleRef);
      return new ComponentFixture<T>(componentRef, ngZone, autoDetect);
    // ...
  }

Donc, nous devons aller de l'avant et vérifier l'implémentation de la classe ComponentFixture dans le code source :

export class ComponentFixture<T> {
  // The DebugElement associated with the root element of this component.
  debugElement: DebugElement;

  // The instance of the root component class.
  componentInstance: T;

  // The native element at the root of the component.
  nativeElement: any;

  // The ElementRef for the element at the root of the component.
  elementRef: ElementRef;

  // ...
  constructor(
      public componentRef: ComponentRef<T>, public ngZone: NgZone|null,
      private _autoDetect: boolean) {
    this.changeDetectorRef = componentRef.changeDetectorRef;
    this.elementRef = componentRef.location;
    // ...

Nous pouvons voir que elementRef est une propriété de la classe ComponentFixture qui initialise le constructeur.

Et enfin, résumant ce qui précède - nous avons la réponse: ElementRef qui est injecté au composant dans le constructeur est en réalité un wrapper autour de l’élément DOM. L'instance injectée de ElementRef est une référence à l'élément Host du composant actuel. Suivez ce message StackOverflow _ pour obtenir plus d'informations à ce sujet.

C'est pourquoi, dans le constructeur de composant console.log, nous voyons l'instance de ElementRef et non l'instance de MockElementRef. Ainsi, ce que nous avons réellement fourni dans le tableau de fournisseurs TestBed - n’est qu’une autre instance de ElementRef basée sur MockElementRef

2
shohrukh