繁体   English   中英

Angular 4:模拟 ElementRef

[英]Angular 4: Mock ElementRef

我试图弄清楚如何模拟注入到组件中的ElementRef 我的组件如下:

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

我的测试规范如下:

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

  ...
});

运行测试时, console.logapp.component.ts的构造函数中的输出为:

控制台输出

如您所见,它注入的是MockAppService而不是MockElementRef (即使它们都以相同的方式模拟)。

这篇SO 帖子建议像设置任何其他模拟一样进行设置,但是我注意到这是针对 Angular 2 的——所以我想知道 Angular 4 中的情况是否发生了变化?

可以在此处找到具有上述代码和 Jasmine 测试的 Plunker。 运行 Plunker,然后单击“运行单元测试”链接以启动单元测试。 控制台输出可以在开发者工具/Firebug 中观察到。

简短的回答 -这是设计使然:)

让我们逐步深入了解更长的答案,并尝试弄清楚 - 当我们通过TestBed配置测试模块时,幕后发生了什么。

步骤1

根据test_bed.ts的源代码:

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

正如我们所看到的 - configureTestingModule方法只是将提供的实例推送到this._providers数组中。 然后我们可以说:嘿, TestBed ,给我这个提供者ElementRef

  // ...
  let elRef: ElementRef;

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

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

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

在控制台中,我们将看到:

在此处输入图像描述

第一个控制台是从组件构造函数中记录的,第二个控制台是从测试中记录的。 所以,看起来我们正在处理两个不同的ElementRef实例 让我们继续。

第2步

让我们看另一个例子,假设我们有一个注入ElementRef和我们之前创建的其他一些自定义服务AppService的组件:

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

当我们测试这个组件时——我们必须提供AppService (服务本身或其模拟),但是,如果我们不向ElementRef提供TestBed测试永远不会抱怨这个: NullInjectorError: No provider for ElementRef! .

因此,我们可以建议, ElementRef看起来不像依赖项,并且始终链接到组件本身。 我们离答案越来越近了。 :)

第 3 步

让我们仔细看看TestBed如何创建组件: TestBed.createComponent(AppComponent) 这是源代码的一个非常简化的版本:

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

因此,我们必须继续检查源代码ComponentFixture类的实现:

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;
    // ...

我们可以看到, elementRefComponentFixture类的一个属性,它被构造函数初始化。

最后,总结以上内容 - 我们得到了答案:在构造函数中注入组件的ElementRef实际上是 DOM 元素的包装器。 ElementRef的注入实例是对当前组件的宿主元素的引用。 关注此StackOverflow 帖子以获取有关它的更多信息。

这就是为什么在组件构造函数 console.log 中我们看到MockElementRef的实例而不是ElementRef的实例。 所以,我们在 TestBed 提供者数组中实际提供的只是另一个基于MockElementRefElementRef实例。

如果有人在 Angular 环境中使用@ngneat/spectator 可以使用以下方法访问 ELementRef:

const elementRef = new Spectator().debugElement;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM