概要
Angular での開発において、DOM 操作などのために window.document を参照することはあるかと思います。
@angular/common には、DOCUMENT という DI Token が用意されています。こちらを利用することで、依存注入経由で document を参照することができますし、またテストも書きやすくなります。
※ この記事を執筆してる時点での Angular の最新バージョンは v13.1.2 になります。
サンプルコード
以下、 DOCUMENT 経由で querySelector を利用するような Service クラスのサンプルコードです。
import { DOCUMENT } from '@angular/common';
import { Inject, Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
class FooService {
constructor(
@Inject(DOCUMENT) private document: Document
) {}
prependSomething() {
const foo = this.document.querySelector('#foo')
foo.prepend('something')
}
}
グローバル空間である window に直接依存しなくなりましたね。
依存注入を利用したので、テストが書きやすくなります。
以下、 DOCUMENT を依存注入して querySelector をモックするようなサンプルテストコードです
import { DOCUMENT } from '@angular/common';
import { getTestBed, TestBed } from '@angular/core/testing';
import { FooService } from './foo.service.ts'
describe('FooService', () => {
let service: FooService;
const mockElement = {
prepend: jasmine.createSpy('prepend'),
// 必要な spy はここに適宜追加してください
}
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
FooService,
{ provide: DOCUMENT, useValue: { querySelector: () => mockElement } },
],
});
service = getTestBed().inject(DownloadReportDialogService);
})
it('prependSomething', () => {
service.prependSomething()
expect(mockElement.prepend).toHaveBeenCalledWith('something')
})
})
document を configureTestingModule の中で依存注入したので、window.document をテストコードから直接参照せずに済みました。
また、window.document を直接触るような場合、他のテストに影響を与えないように、適切なリセット処理が必要になり、少し面倒くさい…という問題があります。
以上、「@angular/common の DOCUMENT を利用して、window.document を依存注入する」でした。
参考URL
- 公式ドキュメント: Angular - DOCUMENT
- Medium の解説記事: How to Inject Document in Angular | by Jared Youtsey | ngconf | Medium