コンポーネントにインジェクトされる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.log
のコンストラクターのapp.component.ts
からの出力は次のようになります。
ご覧のとおり、それはMockAppService
を注入していますが、MockElementRef
は注入していません(両方とも同じようにモックされています)。
この SOの投稿が示唆する 他のモックと同じように設定しますが、これはAngular 2- in Angular 4?
上記のコードとJasmineテストを含むPlunkerは here にあります。プランカーを実行し、ユニットテストを開始するために「ユニットテストを実行」リンクをクリックします。コンソール出力は、開発者ツール/ 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);
});
_
コンソールには以下が表示されます。
最初のコンソールはコンポーネントコンストラクターから記録され、2番目のコンソールはテストから記録されました。だから、2つのコンソールElementRef
の異なるインスタンス。次へ移りましょう。
ステップ2
別の例を見てみましょう。ElementRef
と以前に作成した他のカスタムサービスAppService
を注入するコンポーネントがあるとします。
_export class HelloComponent {
constructor(private _elementRef: ElementRef, private _appService: AppService) {
console.log(this._elementRef);
console.log(this._appService);
}
}
_
このコンポーネントをテストするとき-AppService
(サービス自体またはそのモック)、[〜#〜] but [〜#〜]を提供する必要があります。ElementRef
をTestBed
に提供しない場合これについて文句を言うことはありません:_NullInjectorError: No provider for ElementRef!
_。
したがって、ElementRef
は依存関係のようには見えず、常にコンポーネント自体にリンクされていると提案できます。答えに近づいています。 :)
ステップ
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;
// ...
_
elementRef
は、コンストラクターを初期化するComponentFixture
クラスのプロパティであることがわかります。
最後に、上記の要約-答えがあります:コンストラクターのコンポーネントに注入されるElementRef
は、実際にはDOM要素のラッパーです。挿入されたElementRef
のインスタンスは、現在のコンポーネントのHost要素への参照です。詳細については、この StackOverflow post に従ってください。
これが、コンポーネントコンストラクターconsole.logでElementRef
のインスタンスではなく、MockElementRef
のインスタンスを見る理由です。したがって、TestBedプロバイダー配列で実際に提供したのは、ElementRef
に基づくMockElementRef
の単なる別のインスタンスです。