首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >角4:模拟ElementRef

角4:模拟ElementRef
EN

Stack Overflow用户
提问于 2017-12-18 16:51:14
回答 2查看 27.5K关注 0票数 27

我正在努力弄清楚如何模拟注入到组件中的ElementRef。我的组成部分如下:

app.component.ts:

代码语言:javascript
复制
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:

代码语言:javascript
复制
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 (尽管它们都是以相同的方式被嘲弄)。

这个所以post建议用来设置它,就像其他任何模拟一样,但是我注意到这是角2,所以我想知道在角4中是否发生了变化?

具有上述代码和茉莉花测试的柱塞可以找到这里。运行柱塞,然后单击“运行单元测试”链接,以启动单元测试。控制台输出可以在developer tools/Firebug中观察到。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-08-30 17:41:31

答案很简单--这是故意的:)

让我们一步一步地深入到更长的答案中,并试图找出--当我们通过TestBed配置测试模块时,引擎罩下发生了什么。

步骤1

根据bed.ts的源代码

代码语言:javascript
复制
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给我

代码语言:javascript
复制
  // ...
  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

代码语言:javascript
复制
export class HelloComponent  {
  constructor(private _elementRef: ElementRef, private _appService: AppService) {
    console.log(this._elementRef);
    console.log(this._appService);
  }
}

当我们测试这个组件时--我们必须提供AppService (服务本身或它的模拟)、但是,如果我们不向TestBed提供ElementRef --测试将不会抱怨这一点:NullInjectorError: No provider for ElementRef!

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

步骤3

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

代码语言:javascript
复制
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类在源代码中的实现。

代码语言:javascript
复制
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中,我们看到的是ElementRef实例,而不是MockElementRef实例。因此,我们在TestBed提供者数组中实际提供的只是基于MockElementRefElementRef的另一个实例。

票数 21
EN

Stack Overflow用户

发布于 2022-06-21 08:38:01

万一有人在角env中使用@ngneat/spectator。可以通过以下方式访问ELementRef:

代码语言:javascript
复制
const elementRef = new Spectator().debugElement;
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47872946

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档