とんかつ時々あんどーなつ

〜たとえ低空でも飛行していられるように〜

NO_ERRORS_SCHEMA を安易に使うのをやめたい話

この記事は Angular アプリケーションでコンポーネントのテストを書くときに安易に NO_ERRORS_SCHEMA を使いすぎていたという反省とそれをやめたいという話である。

NO_ERRORS_SCHEMA を使っていた背景

Angular でアプリケーションを作っているとコンポーネントの中にコンポーネントを配置することは日常茶飯事である。 例えば HomeComponent というコンポーネントで HomeChildComponent を表示しているとする。

<p>home works!</p>
<app-home-child></app-home-child

home.component.spec.ts はデフォルトのままだと以下のようになっている。

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HomeComponent } from './home.component';

describe('HomeComponent', () => {
  let component: HomeComponent;
  let fixture: ComponentFixture<HomeComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [HomeComponent],
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(HomeComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

この状態でテストを実行すると「NG0304: 'app-home-child' is not a known element」という console error が発生する。これはテストファイルが app-home-child という要素を知らず、未知の要素と判断したときに発生する。

今回の HomeChildComponent のように自分で Angular のコンポーネントとして作成している場合にこのエラーを回避する一番簡単な方法は declarations に HomeChildComponent も追加することだ。

...
describe('HomeComponent', () => {
...
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [HomeComponent, HomeChildComponent],
    }).compileComponents();
  });
...
});

declarations に追加することで既知の要素になるのでエラーが発生しなくなる。しかし、これでは別の問題が発生する場合がある。

仮に HomeChildComponent が別のコンポーネントやモジュールに依存している場合にそれらを declarations や imports に追加することになる。 だが、親コンポーネントが子コンポーネントの依存を把握するのはさすがにやりすぎだと感じる。

そのような場合にテンプレート内に出てきた未知の要素を無視してもらうために NO_ERRORS_SCHEMA を使っていた。

NO_ERRORS_SCHEMA の代わりに CUSTOM_ELEMENTS_SCHEMA を使う

ドキュメント https://angular.jp/api/core/NO_ERRORS_SCHEMA を読むと、未知の要素だけでなく未知のプロパティも無視できると書いてある。 しかしこれは本来無視してはいけないものまでまとめて無視する可能性がある。NO_ERRORS_SCHEMA を使うことは推奨されておらず、代わりに CUSTOM_ELEMENTS_SCHEMA を使うことを推奨している。

CUSTOM_ELEMENTS_SCHEMA はダッシュケースで命名されている要素を許可してくれる。Angular アプリケーション上で作るコンポーネントも基本的にはこの命名規則に従っているはずなので NG0304 の問題は解消できる。

CUSTOM_ELEMENTS_SCHEMA で許可できないもの

NO_ERRORS_SCHEMA では無視していたが CUSTOM_ELEMENTS_SCHEMA にすると無視できないエラーに「NG0303: Can't bind to 'routerLink' since it isn't a known property of 'a'.」がある。これは a 要素に未知の routerLink があるときに発生するエラーである。CUSTOM_ELEMENTS_SCHEMA はダッシュケースの要素のみ許可するため routerLink を無視できないのは自明である。

routerLink を使うときは RouterModule を import しているはずなので、テスト時にも RouterTestingModule を import して環境を作るほうが適切だと思う。よってやはりこの場合も NO_ERRORS_SCHEMA を使う理由にはならない。

まとめ

テストコードを書く際にいろんなところで NO_ERRORS_SCHEMA を使ってエラーを解消してきたが、推奨されていないことがわかった。今回 NO_ERRORS_SCHEMA を使わないといけないパターンまではわからなかったが、必要ない限り安易に使うのは控えようと思った。