KAKEHASHI Tech Blog

カケハシのEngineer Teamによるブログです。

【Angular】@angular/common の DOCUMENT を利用して、window.document を依存注入する

概要

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