13
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

テストコードにもLinterを導入しよう

Last updated at Posted at 2023-06-26

はじめに

この記事はニジボックスQiita記事投稿リレーの1日目の記事として書きました。
きっかけはSlackで「投稿リレーやってみたい」と呟いたらいい感じにメンバーが集って実際に投稿リレーをやることとなりました。
思いつきの企画にも乗ってくれるメンバーがいる環境に感謝したいです。
自分の投稿のあとにも記事が公開されると思いますので、もしよろしければ今後公開される記事も見ていただけると幸いです。

本題

最近ではフロントエンドでもコンポーネントの単体テスト・結合テストなどが一般的になりましたが、どのようにテストコードを書けばいいのか自信がない人も多いかと思います。

自分もあまりテストコードを書くことに自信があるという訳ではなかったのですが業務で単体テストを書くことになり、なるべく質の高いテストコードを書けるようeslint-plugin-jestというjest向けのESLintプラグインも導入しました。
推奨ルールからいくつかカスタマイズした部分もあるので、カスタマイズ内容とその理由についてまとめさせていただきます。

eslint-jestとは

前記の通り、ESLintのjest向けのプラグインです。
主にテストコードの書き方を統一するために導入しました。

ルールのカスタマイズについて

推奨のルール(jest/recommended)だけでも良かったのですが、テストの書き方を共通化してレビュー時の負担を減らす意味合いでも追加でルールを定義して制約を強めることにしました。

eslintrc.js
// ...
overrides: [
  {
    files: ['**/*.test.tsx', '**/*.test.ts'],
    plugins: ['jest'],
    env: {
      'jest/globals': true,
    },
    extends: ['plugin:jest/recommended'],
    // https://github.com/jest-community/eslint-plugin-jest#rules
    rules: {
      // expectは最大2つまで
      'jest/max-expects': ['error', { max: 2 }],
      // test/itではtestを利用する
      'jest/consistent-test-it': ['error', { fn: 'test', withinDescribe: 'test' }],
      // テストケースは必ずdescribeの中に定義されている必要がある
      'jest/require-top-level-describe': ['error'],
      // describeは3回以上ネストしてはいけない
      'jest/max-nested-describe': ['error', { max: 3 }],
      // hooksは必ずテストケースの前に記述する必要がある
      'jest/prefer-hooks-on-top': ['error'],
      // 関数をモックする場合 jest.spyOn() を優先的に使用するようにする
      'jest/prefer-spy-on': ['error'],
      // 組こ込みのMatcherが使える場合は自動修正を提案する
      'jest/prefer-equality-matcher': ['warn'],
    },
  },
],
// ...

上記が実際のeslint-jestの設定です。
コメントも入れたのでざっくりと眺めてもらうだけでもなんのためのルールなのかというのが分かるかもしれませんが、中でもテスト対象の明確化とテストの構造化のためのルールが個人的にはコアな部分になると思っているのでそちらについて紹介させていただきます。

テスト対象を明確にするためのルール

一つのテストコードで複数のアサーションを行うと、何に対してのテストを行っているかが分かりづらくなってしまいます。
jest/max-expectsはテストコード内で使用できるexpectsの最大数を制限できるルールで今回は2個までを上限として設定しました。

// expectは最大2つまで
'jest/max-expects': ['error', { max: 2 }],

このルールを導入することで、1つのテストコードにどんどんアサーションが追加されていき何に対してテストを行っているのかが分からなくなってしまう、という状態を防ぐことができます。

テストの構造化について

下記の2項目は主にテストの構造化についての制約としてルールを追加しました。

// テストケースは必ずdescribeの中に定義されている必要がある
'jest/require-top-level-describe': ['error'],
// describeは3回以上ネストしてはいけない
'jest/max-nested-describe': ['error', { max: 3 }],

このルールによりテストコードは1つ以上3つ以下のdescribeの中にしか記載できなくなります。
単体テストはstorybook+ testing-library + jestの組み合わせで主にコンポーネントに対して行うため下記のようなテストの構造を理想としてこのようなルールを設けました。

index.test.ts
import { render, screen } from '@testing-library/react'
import { composeStories } from '@storybook/react'
import * as stories from './index.stories'
import '@testing-library/jest-dom/extend-expect'

describe('<機能名>の機能を提供する<コンポーネント名>コンポーネント', () => {
  const { Default } = composeStories(stories)

  // テストの対象などによってまとめる
  // ex) DOMの存在確認やイベントハンドラーの実行確認など
  describe('<テストカテゴリー>', () => {
    test('<テスト内容>', () => {
      render(<Default />)
      expect(true).toBe(false)
    })
    test('<テスト内容>', () => {
      render(<Default />)
      expect(true).toBe(false)
    })
  })
})

その他の項目

その他の項目としてはtest()it()どちらを使うか、組み込みのマッチャーが使える場合は修正を提案するなど、テストコードの書き方を統一するためのルールなども導入しました。

ルールについてはリポジトリのREADME.mdに記載されているためチームのテスト方針に合ったルールが無いかなど見てみるといいかもしれません。

このようにテストコードにある程度の制約を設けることで、ある程度読みやすいテストコードを書けるようになったと感じています。
また、単純にコードのクオリティアップという側面だけでなく自分自身でテストコードの構造化や何を保証するためのテストコードなのか、という部分も考える良いきっかけになったと感じています。

この記事はニジボックス投稿リレー企画、1日目の記事です。
次の投稿は@ment_REさんの「【TypeScript】超実践的テクニック集【Reactなし】」です!

13
4
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
13
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?