UIコンポーネントのテストパターン
はじめに
インテグレーションテスト
概要
DOMなどの外部の要素・モジュールとのやり取りなども含めた振る舞いを検証します 基本的にjsdomやhappy-domなどが使用されることが多いと思うため、ブラウザーとの互換性を100%保証できるものではないため、必要に応じてE2Eなどの手法と併用するとよいと思います レンダリング結果のCSSセレクターによる要素の問い合わせ/shallow rendering/コンポーネントの状態の検証や直接的なメソッド呼び出しなどの様々な機能をサポートします
柔軟・高機能ではあるものの、気をつけて利用しないと、実装の詳細に強く依存したホワイトボックステストになりがちなので注意が必要です CSSセレクターによる問い合わせ/コンポーネントの状態の検査やメソッドの呼び出し/shallow renderingといった機能を意図的に提供しません
こういった機能を意図的に排して、アクセシビリティに基づいた要素の問い合わせなどのみを提供することで、信頼性の高いテストを記述できるようにしてくれます code:testing-library.spec.js
import { userEvent } from '@testing-library/user-event';
import { render } from '@testing-library/react';
test('SomeComponent', async () => {
const { getByRole, queryByRole } = render(<SomeComponent {...props} />);
const user = userEvent.setup();
expect(queryByRole('link', { name: 'foo' })).not.toBeInTheDocument();
await user.click(getByRole('button', { name: 'OK' }));
expect(getByRole('link', { name: 'foo' })).toBeVisible();
});
ユニットテスト
Reactのコンポーネントは、副作用を含まぬように記述されていれば、propsを受け取ってVNodeを返す純粋関数と変わりない(pure components)ため、容易にユニットテストが記述できます ややマイナーですが、ritewayというテストフレームワークではこのアプローチが採用されています ※個人的な意見
フロントエンド開発においては、ヘッドレスブラウザーを利用して、バックエンドとの疎通なども含めたUI全体の振る舞いをテストされるケースが多いのではないかと思います
UIのルック・アンド・フィールをテストする上でとても有用な仕組みだと思います
一見、非常に便利な仕組みではあるものの、本質的に偽陰性を生みやすい仕組みでもあると思うため、乱用には注意が必要 振る舞いを細かくテストしたいケースにおいては、インテグレーションテストやCypress/Playwrightなどの方が得意だと思うため、これらの手法とうまく併用するのがよいと思います どのパターンに従ってテストを書くとよいのか?
基本
例)
実行コストが高い (実行に時間がかかる、インフラやCIなどのセットアップに手間がかかる)
記述・メンテナンスコストが他のテスト手法よりも高い (場合によっては、メンテナンスができず、負債になってしまう可能性もある)
不安定なテスト(flaky tests)ができやすい
それぞれに得意・不得意があるため、特定のパターンや手法にこだわるよりは、必要に応じてそれぞれのパターンを併用するのがよいと思います
後からテストを導入する場合
もしまだテスト自動化が行われていないプロジェクトに対して後からテストを導入していきたいような場合は、まずはインテグレーションテストやE2Eテストなどのコードベースの広い部分をカバーできる手法から採用していくとよいと思います この場合、コードベース的にユニットテストが書きやすい構成にはなっていない可能性もあるとは思うため、まずはインテグレーションテストなどの広い部分をカバーできる手法から導入していった方がやりやすいのではないかと思います 注意点
これらのAPIを使うことで特定のサードパーティパッケージをスタブすることができ、非常に便利な機能だと思います
ただし、これらのAPIを利用したテストは本質的にホワイトボックステストに陥ってしまい、信頼性が低下してしまいやすいと思います なので、特別な理由がない限り、極力これらのAPIの使用は避けたほうがよいと思っています (これらのAPIを使わないとテストが書くのが困難な場合など)
具体的には、jest.mock/vi.mockを使わずともテストがかけるよううまくコードを分離・独立させるか、または対象のサードパーティパッケージを利用したコードもカバーするようなインテグレーションテストを記述するのがよいと思います