🧪

フロントエンド開発のためのテスト入門 読書ログ

2024/02/07に公開

はじめに

  • この記事では、「フロントエンド開発のためのテスト入門」の第5章までの内容について、私の読書ログを残します。
    • 第5章までとした理由は、後半の章で触れられている Next.js や Storybook に関する部分については、読書ログと最新のバージョンで記法が異なる可能性の方が高いためです。
    • 例えば、Storybook については、現行では、Storybook 7.6 - November 2023がリリースされています。
  • フロントエンド開発のためのテストについて、体系的に解説されており、とても分かりやすい本でした。

https://github.com/frontend-testing-book

第1章 テストの目的と障壁

  • テストにより健全なコードを維持可能。
  • そのためにも、テストコードは必要なものであるという認識をチームで持ち、プランニング時には、テストコードの実装時間の確保が必要。
  • チームがテストコードを書くかどうかは、設計初期の段階で決まるため、テストコードの前例を示す。
    • 前例があれば、チームのメンバの習得速度が向上する。
    • 前例を示すためにも、本書で様々なテスト手法のコツを掴む。
  • どれだけテストコードを書くべきかチームで検討する。

第2章 テスト手法とテスト戦略

テストの範囲

テストのタイプ

  • インタラクションテスト
    • UIコンポーネントの操作を検証。
    • Reactなどではブラウザ無しでもテスト可能。
    • スクロールやセッションストレージを使用する場合は、ヘッドレスブラウザ+UIオートメーションで実施。
  • アクセシビリティテスト
    • 心身特性に隔てのない製品を提供できているか検証。
  • ビジュアルリグレッションテスト
    • 差分検出により不具合が発生していないか検証。

テスト戦略モデル

  • アイスクリームコーン型

    • 上層のテストを充実させるが、不安定なテストとなるため、アンチパターン。
  • テストピラミッド型

    • 安定する高速なテストを実現するため、下層のテストを充実させる。
  • テスティングトロフィー型

    • 単体のUIコンポーネントだけで成立する機能はほとんど無いため、結合テストを充実させる。
  • テスト対象と目的を判断することが重要。

  • 過剰に書いたテストを減らすことも必要。

    • 本書は学習向けに手厚く書かれている。

第3章 はじめての単体テスト

テストの構成要素

同期処理

  • 通常
describe(グループ名, () = {
  test(タイトル, () => {
    expect(検証値).マッチャー(期待値)
  })
})
  • 例外
expect(() => 検証値).toThrow()

非同期処理

  • 通常
test(タイトル, async () => {
  expect(await 検証値).マッチャー(期待値)
})
  • 例外
test(タイトル, async () => {
  expect.assertions(1)
  try {
    await 検証値
  } catch (err) {
    expect(err).マッチャー(期待値)
  }
})

第4章 モック

スタブ

  • 定められた値を返すもの。

同期処理

jest.mock(パス, () => {
  ...jest.requireActual(対象パス),
  関数名: アロー関数,
})
jest.mock(パス)
jest.spyOn(オブジェクト, 関数名)

非同期処理

jest.spyOn(オブジェクト, 関数名).mockResolvedValueOnce(返り値)

スパイ

  • 関数やメソッドの呼び出し(回数、引数、出力)を記録・確認するもの。
const mockFn = jest.fn()
mockFn('spy')
// expect(mockFn).toHaveBeenCalledTimes(1)
// expect(mockFn).toHaveBeenCalledWith('spy')

第5章 UIコンポーネントテスト

ライブラリ

  • jest-environment-jsdom
    • Jest 実行環境の Node.js のため、テスト環境用の DOM が必要。
  • @testing-library/react
    • React コンポーネントのテスト。
  • @testing-library/jest-dom
    • UI コンポーネント用の便利なマッチャーの追加。
  • @testing-library/user-event
    • ユーザがブラウザを使用する際にブラウザで起こる実際のイベントをシミュレート可能。

UIコンポーネントテスト

  • Testing Library では、DOMの要素を暗黙的なロールにより識別可能。

存在すること

import { render, screen } from '@testing-library/react'

test(タイトル, () => {
  render(<Container />)
  expect(screen.getByText('テスト')).toBeInTheDocument()
  expect(screen.getByRole('heading').toHaveTextContent('テスト'))
  const list = screen.getByRole('list')
  expect(list).toBeInTheDocument()
  expect(within(list).getAllByRole('listitem')).toHaveLength(3)
})

存在しないこと

test(タイトル, () => {
  render(<Container />)
  const list = screen.queryByRole('list')
  expect(list).not.toBeInTheDocument()
})

文字入力

import userEvent from '@testing-library/user-event'
const user = userEvent.setup()

test(タイトル, () => {
  render(<Container />)
  const textbox = screen.getByRole('textbox', { name: 'タイトル' })
  await user.type(textbox, 'タイトル')
  expect(screen.getByDisplayValue('タイトル')).toBeInTheDocument()
})

スナップショット

  • toMatchSnapshot により、snapshots 配下に .snap ファイルが作成される。
  • $ npx jest --updateSnapshot によりスナップショットを更新可能。
test(タイトル, () => {
  const { container } = render(<Container />)
  expect(container).toMatchSnapshot()
})

ロールとアクセシブルネームの確認

import { logRoles, render } from '@testing-library/react'

test(タイトル, () => {
  const { container } = render(<Container />)
  logRoles(container)
})
shinaps テックブログ

Discussion