我正试图找出如何模拟注入组件的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);
  }
}

我的测试规范如下:
应用组件规范:
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.log的构造函数中的app.component.ts的输出为:
angular - Angular 4:模拟ElementRef-LMLPHP
如您所见,它注入的是MockAppService,而不是MockElementRef(尽管它们都是以相同的方式模拟的)。
这个SO post suggests来设置它,就像其他的模拟一样,但是我注意到这是针对角度2的-所以我想知道在角度4中是否发生了变化?
有上述代码和茉莉花测试的劫匪可以找到here。运行plunker,然后单击“运行单元测试”链接以启动单元测试。控制台输出可以在developer tools/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,给我这个提供者:
  // ...
  let elRef: ElementRef;

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

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

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

在控制台中,我们将看到:
angular - Angular 4:模拟ElementRef-LMLPHP
第一个控制台是从组件构造函数中记录的,第二个控制台是从测试中记录的。
所以,我们似乎在处理两个不同的ElementRef实例。我们继续吧。
步骤2
让我们看看另一个例子,假设我们有一个注入ElementRef的组件和我们以前创建的一些其他定制服务ElementRef
export class HelloComponent  {
  constructor(private _elementRef: ElementRef, private _appService: AppService) {
    console.log(this._elementRef);
    console.log(this._appService);
  }
}

当我们测试这个组件时-我们必须提供AppService(服务本身或它的模拟),但是,如果我们不提供AppServiceElementRef-测试永远不会抱怨这个:TestBed
因此,我们可以建议,NullInjectorError: No provider for ElementRef!看起来不像依赖项,总是链接到组件本身。我们离答案越来越近了。:)
步骤3
让我们进一步了解一下ElementRef如何创建组件:TestBed。这是源代码的一个非常简化的版本:
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);
    // ...
  }

因此,我们必须继续检查source codeTestBed.createComponent(AppComponent)类的实现:
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;
    // ...

我们可以看到,ComponentFixture是由构造函数初始化的elementRef类的属性。
最后,总结一下上面的内容-我们得到了答案:ComponentFixture注入构造函数中的组件实际上是dom元素的包装器。ElementRef的注入实例是对当前组件的宿主元素的引用。按此StackOverflow post获取有关它的更多信息。
这就是为什么在component constructor console.log中可以看到ElementRef的实例,而不是ElementRef的实例。因此,我们在testbed providers数组中实际提供的只是基于MockElementRefElementRef的另一个实例。

关于angular - Angular 4:模拟ElementRef,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47872946/

10-15 05:34