フロントエンド開発においてもテスト自動化はメジャーなものとなってきました。テストの必要性や方法論を掴むため、まずは以下の文献がおすすめです🦁
テストは大きく分類すると
- 静的(Static)テスト
- 単体(Unit)テスト
- 結合(Integration)テスト
- E2E(end to end)テスト
にわけられ、名称や範囲の違いからシナリオテストやビジュアルリグレッションテストなどが存在します。 詳細はこちらをご覧ください。
一方で、テスト自動化の導入はコストは決して低くなくメンテナンスの問題が否めません。
メジャーになってきた傾向のあるフロントエンドのテストですが、テストに慣れていないメンバーが多い場合記述コストが高くつき過ぎてしまうケースも否めません。
たしかなことは費用対効果が高ければ導入すべきということです。(資料🦁)
上記を全体的に鑑み、本リポジトリでは記述コストとメンテナンスコストをできるだけ低く漸進的なテストの導入を目指します。
基本的に先ほどの資料を踏襲します。
- トレードオフの観点でバランスのよい結合テストを厚めに書く
- E2E テストは、課金導線やタイムラインなどの、不具合が発生するとビジネス上のネガティブインパクトの大きい箇所だけ書く
- 単体テストは、明らかにテストしなくても自明なロジックに対しては書かない。複雑性が高いビジネスロジックの関数に関しては書く
- 静的テストはベースラインとして必ず引く。導入が後回しになればなるほど導入コストが跳ね上がるので、プロジェクトの最初に必ず入れる
これをベースに本リポジトリに落とし込んでいきましょう。
model-component
に対して実行します。したがって対象は以下です。
src/components/features
src/component/pages/Component
src/component/pages/Component/components/Component
features
には再利用可能なmodel-component
が格納されているため基本的に記述します。
結合テストの区分は曖昧であるためpages
のテスト戦略は場合によって異なります。API通信やビジネスロジックがトップのpages/Component
に集中しているのであればこちらに記述すると良いでしょう。
一方で、pagesはview-component
のようにレイアウト的な役割が主なこともあるはずです。そのような切り分けの場合は子ディレクトリにmodel-componentが集中するはずですのでそちらにテストを書いていきましょう。
ツールはJest
とtesting-library
を用います。
基本的に必須ではありません。記述する場合は「不具合が発生するとビジネス上のネガティブインパクトの大きい箇所」に追加します。
ツールにはPlaywright
を用います。
グローバルなhooks
に対して記述します。各コンポーネントのロジックは結合テストでカバーされているため書かない想定です。
例外として、ロジックのあるようなparts
には記述した方が良いかもしれません。例を挙げるとスナックバーやモーダルなどになります。
ツールにはJest
とtesting-library
を用います。
本プロジェクトには以下のツールを導入しています。
それぞれの使い方と参考文献を以下に記載します。
カタログツールです。src/parts
配下のコンポーネントを管理し再利用性を高めます。
最近ですとStorybook Playfunction
という機能が追加されテストの実行や可視化ができるようになりました。
大変便利な機能ではあるのですが、Storybookは破壊的な変更が多くまたストーリーの管理にもコストがかかります。
費用対効果を考えた際に重要なことは「コンポーネントの管理と再利用性の向上」であると考え、本リポジトリではカタログとテストは分離しコンポーネントの管理に専念してもらうこととしました。
後述するyarn parts
コマンドを実行すると.story
ファイルが自動生成されます。
import { action } from '@storybook/addon-actions'
import type { ComponentMeta, ComponentStoryObj } from '@storybook/react'
import { ComponentName } from './ComponentName'
export default {
component: ComponentName,
} as ComponentMeta<typeof ComponentName>
export const Default: ComponentStoryObj<typeof ComponentName> = {
args: { text: 'サンプルボタン', handleClick: action('ボタン押下') },
parameters: {
docs: {
description: {
component: 'Some component _markdown_',
},
},
},
}
export const Story: ComponentStoryObj<typeof ComponentName> = {
args: { text: 'サンプルボタン', handleClick: action('ボタン押下') },
parameters: {
docs: {
description: {
story: 'Some story **markdown**',
},
},
},
}
yarn parts
時にコンポーネント名の入力が求められ、そちらの内容がComponentName
として扱われ雛形が出力されます。
したがって私たちが主に追記するのはargs
部です。サンプルコードのtext
やhandleClick
がコンポーネントのprops
に対応していますので、作成したコンポーネントに合わせて修正してください。
parameters.docs.description
はStorybookのドキュメントに表示される説明文です。component
はコンポーネントの、story
はストーリーの説明文になります。
場合に応じてStory
を増やし、バリエーションを増やすことが可能です。
参考文献
- Component Story Format 3.0
- TypeScript + Storybook CSF3.0の書き方とユニットテストへの応用
- TypeScript x React x Storybook のプロジェクトを CSF3.0 対応させようとして型問題でテンパったら読む記事
API通信をモック化するツールです。開発時やテストの際に仮のAPIサーバーとして用います。そもそもなぜこちらのツールを使うのか。という点についてはこちらをご覧ください。なぜMock Service Workerなのか。入門編
.mocks
フォルダに必要なファイルが格納されており、主にhandler.ts
を編集しモックしたいエンドポイントとレスポンスを追加します。.mocks/index.ts
をからエクスポートされているserver
もしくはworker
が実行されることによりモッキングが開始します。
本リポジトリではあらかじめ_app.tsx
にてworker.start()
が動くよう設定されていますので、開発時はhander.ts
に記述を追加していくだけでモックAPIが追加可能です。
テスト時はコンポーネント単位、またnode環境で実行されるため別途mswを起動する必要があります。
こちらを参考に以下のようなコードを追加しましょう。
describe("コンポーネントのテスト", () => {
// Establish API mocking before all tests.
beforeAll(() => server.listen());
// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.
afterEach(() => server.resetHandlers());
// Clean up after the tests are finished.
afterAll(() => server.close());
//以下にテストケースを記述
});
英語になってしまいますが、詳しい使い方は公式ドキュメントのDocsやExampleがよくまとまっています。また、実践的な使い方についてはこちらもご覧ください。
参考文献
単体、結合テスト用のライブラリです。本リポジトリでは主にsrc/components/features
に対する結合テスト、src/hooks
への単体テストに用います。
はじめにJest
とtesting-library
別のライブラリだということを抑えてください。
まず、Jest
はJavascript用のテストランナーであり以下のような関数のテストができます。
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
Nodeサーバーならこれで十分かもしれませんが、私たちの目指すゴールはReactのコンポーネントをテストすることです。したがって、testing-library
を導入することによりJSXのレンダリングやHooksが動く環境を作りコンポーネントの要素取得やインタラクティブなテストを実現します。
import { Sample } from "@/components/Sample";
import { render } from "@testing-library/react";
describe("Sampleコンポーネント", () => {
test("should first", () => {
const { getByText } = render(<Sample />);
expect(getByText("Nextjs+Jestのサンプルサプリ")).toBeTruthy();
expect(getByText("設定がすごく楽になりました。")).toBeTruthy();
});
});
https://zenn.dev/miruoon_892/articles/e42e64fbb55137 より引用
より詳細を掴むにはReactでTesting Library/Jestを使ってテストを学ぼうやJest, testing-libraryの公式ドキュメントがおすすめです。
参考文献
- React Testing Libraryの使い方
- フロントエンド(React Testing Library)で TDD(テスト駆動開発)をする
- Next.js 12でJestの設定がかなり楽になった
- 私のフロントエンドディレクトリ構成・テスト観点 2022
E2Eテスト用のライブラリです。/e2e
に記述します。使い方は公式ドキュメントを参考にしてください。
また、playwrightにはユーザー操作によるテストコード自動生成機能があります。
本リポジトリでは以下のコマンドで実行可能です。
yarn test:e2e:codegen
参考文献