LoginSignup
4
3

More than 3 years have passed since last update.

[Angular] コンポーネントのテストを簡単に書きたい - Testing Library と Spectator

Last updated at Posted at 2021-04-12

はじめに

Angularのコンポーネントテストは、同じようなコードを何度も書いたり、非同期回りがよくわからなかったり、少々つらみがあります。

ここでは、以下の2つのライブラリの書き心地を比較してみたいと思います。

題材

題材として、AngularチュートリアルのTour of Heroesを使います。

インストール方法

npmを使うだけです。

Testing Library

npm install @testing-library/angular --save-dev

Spectator

npm install @ngneat/spectator --save-dev

コンポーネントインスタンス(テスト対象)の生成

オリジナル

オリジナルです。不要なものもある気もしますが長いですね。

  let component: DashboardComponent;
  let fixture: ComponentFixture<DashboardComponent>;
  let heroService;
  let getHeroesSpy;

  beforeEach(
    waitForAsync(() => {
      heroService = jasmine.createSpyObj('HeroService', ['getHeroes']);
      getHeroesSpy = heroService.getHeroes.and.returnValue(of(HEROES));
      TestBed.configureTestingModule({
        declarations: [DashboardComponent, HeroSearchComponent],
        imports: [RouterTestingModule.withRoutes([])],
        providers: [{ provide: HeroService, useValue: heroService }],
      }).compileComponents();
    })
  );

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

Testing Library

render関数を呼ぶことで、表示結果込みのコンポーネントを生成できます。

  let component: RenderResult<DashboardComponent, DashboardComponent>;
  let heroService;
  let getHeroesSpy;

  beforeEach(async () => {
    heroService = jasmine.createSpyObj('HeroService', ['getHeroes']);
    getHeroesSpy = heroService.getHeroes.and.returnValue(of(HEROES));
    component = await render(DashboardComponent, {
      providers: [{ provide: HeroService, useValue: heroService }],
    });
  });

Spectator

最初にファクトリを作ってから、コンポーネントを生成します。

  let component: Spectator<DashboardComponent>;
  const createComponent = createComponentFactory(DashboardComponent);
  let heroService;
  let getHeroesSpy;

  beforeEach(() => {
    heroService = jasmine.createSpyObj('HeroService', ['getHeroes']);
    getHeroesSpy = heroService.getHeroes.and.returnValue(of(HEROES));
    component = createComponent({
      providers: [{ provide: HeroService, useValue: heroService }],
    });
  });

コンポーネントが作成されたかテスト

このあたりの書き方は変わるところないですね。

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

Testing Library / Spectorでテストしているのはコンポーネントなのか?という疑問はあります。

ヘッドラインに'Top Heroes'と出力されているかテスト

オリジナル

it('should display "Top Heroes" as headline', () => {
  expect(fixture.nativeElement.querySelector('h2').textContent).toEqual(
    'Top Heroes'
  );
});

Testing Library

h2の中のテキストを取得します。取得できなければ例外が発生するので、expectしなくてよいです。

it('should display "Top Heroes" as headline', () => {
  component.getByText('Top Heroes', { selector: 'h2' });
});

Spectator

オリジナルより短く書けます。

it('should display "Top Heroes" as headline', () => {
  expect(component.query('h2').textContent).toEqual('Top Heroes');
});

HeroServiceが呼ばれているかテスト

jasmineスパイのテストですので、書き方は変わらないですね。

it('should call heroService', waitForAsync(() => {
    expect(getHeroesSpy.calls.any()).toBe(true);
  })
);

リンクが4つ出力されているかテスト

aタグの数を数えています。

オリジナル

it('should display 4 links', waitForAsync(() => {
    expect(fixture.nativeElement.querySelectorAll('a').length).toEqual(4);
  })
);

Testing Library

queryAllByTextで全マッチして数を数えています。

it('should display 4 links', () => {
  expect(component.queryAllByText(/./, { selector: 'a' }).length).toEqual(4);
});

Spectator

オリジナルより短く書けます。

it('should display 4 links', () => {
  expect(component.queryAll('a').length).toEqual(4);
});

まとめと感想

スター数とダウンロード数は2021年4月12日現在の値です。

Testing Library Spectator
コンポーネント(テスト対象)の生成 TestBedを用意せず、すっぱり書ける TestBedを用意せず、すっぱり書ける
エレメントの取得 get...By、query...Byは、ちょっと癖があるかもしれない ほぼほぼオリジナルのテストと同じ
GitHubスター数 3811 1,413
週間ダウンロード数 17,820 41,504

Angular以外も使っていたり、これから使う可能性がある場合は、ReactやVue等でも使えるTesting Libraryがよさそうです。

Angularのテストに慣れていたり、既存のテストを移行する場合は、Spectatorがわかりやすくてよさそうです。

作成したファイルは https://github.com/sengokyu/ex.angular-testing に置きました。

リンク


  1. 使用している@testing-library/domのスター数は2,530です。 

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3