【Next.js】jest環境構築 + テストコード基礎

July 18, 2022
React / Next / Unit Test / Typescript

これまでVueではそこそこUnit Testを書いてきたのですが、Reactではどうやって書くのだろうか、ということでjestを導入するところからやってみました。

成果物Repo

https://github.com/chanfuku/next-contentful-typescript-blog

まずは環境構築ということで、

Next.js公式examplesにある「next.js/examples/with-jest」を参考にしました。

[環境構築] 各種libraryをinstallします

npm install --save-dev jest @testing-library/react @testing-library/jest-dom @types/jest jest-environment-jsdom

[環境構築] package.jsonにtestコマンドを追加します

  "scripts": {
    "test": "jest",

[環境構築] jest.config.jsを作成します

const nextJest = require('next/jest')

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './',
})

// Add any custom config to be passed to Jest
const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  moduleNameMapper: {
    // Handle module aliases (this will be automatically configured for you soon)
    '^@/components/(.*)$': '<rootDir>/components/$1',

    '^@/pages/(.*)$': '<rootDir>/pages/$1',
  },
  testEnvironment: 'jest-environment-jsdom',
}

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig)

[環境構築] jest.setup.jsを作成します

// Optional: configure or set up a testing framework before each test.
// If you delete this file, remove `setupFilesAfterEnv` from `jest.config.js`

// Used for __tests__/testing-library.js
// Learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect'

これでjestの環境構築は完了したので、テストコードを書いてきます。

tests/にxxx.spec.tsxを作成します

jestはdefaultで__tests__以下に置いてあるファイルあるいは.spec/.textのファイルを見にいくそうです。

next.js/examples/with-jest」も__test__にテストファイルを置いているのでそれを参考にしました。

__tests__/components/search-box.spec.tsxの全体はこちら
import React from "react";
import { render, screen } from '@testing-library/react';
import { Tag } from 'contentful'
import SearchBox from '../../components/search-box'

const allTags: Tag[] = [
  {
    name: 'tag1名',
    sys: {
      id: 'tag1',
      type: 'Tag',
      version: 1,
      visibility: 'public'
    }
  },
  {
    name: 'tag2名',
    sys: {
      id: 'tag2',
      type: 'Tag',
      version: 1,
      visibility: 'public'
    }
  },
  {
    name: 'tag3名',
    sys: {
      id: 'tag3',
      type: 'Tag',
      version: 1,
      visibility: 'public'
    }
  }
]

test('検索ワードが入力された状態で表示されること', () => {
  render(
    <SearchBox
      keyword="キーワード"
      selectedTags={[]}
      allTags={allTags}
      addOrRemove={jest.fn()}
      onKeywordChange={jest.fn()}
    />
  )

  const textbox = screen.getByRole('textbox')
  expect(textbox).toHaveValue('キーワード')
});

test('いくつかのcheckboxがcheckedの状態で表示されること', () => {
  render(
    <SearchBox
      keyword=""
      selectedTags={["tag1", "tag2"]}
      allTags={allTags}
      addOrRemove={jest.fn()}
      onKeywordChange={jest.fn()}
    />
  )

  const checkbox1 = screen.getByRole('checkbox', { name: 'tag1名' });
  expect(checkbox1).toBeChecked()

  const checkbox2 = screen.getByRole('checkbox', { name: 'tag2名' });
  expect(checkbox2).toBeChecked()

  const checkbox3 = screen.getByRole('checkbox', { name: 'tag3名' });
  expect(checkbox3).not.toBeChecked()
});
  • 何をテストしているのか?

URLのリクエストパラメーターに検索ワードとタグIDが入っていて、

その検索ワードとタグIDが検索フォームのテキスト入力とcheckboxにセットされた状態で表示される、という仕様になっているので

検索フォームコンポーネントのpropsに渡された検索ワード/タグIDが、テキスト入力とcheckboxにセットされるかをテストしています。

Image1

  • [ポイントその1] テスト対象のコンポーネントをrenderする

react-testing-libraryを使ってrenderします。

https://testing-library.com/docs/react-testing-library/intro
  render(
    <SearchBox
      keyword="キーワード"
      selectedTags={[]}
      allTags={allTags}
      addOrRemove={jest.fn()}
      onKeywordChange={jest.fn()}
    />
  )
  • [ポイントその2] テスト対象のdomを取得する
const textbox = screen.getByRole('textbox')

要素を取得するためのqueriesというものがあります。

queriesを使ってscreen.getByXXXの様に書くと、要素を取得できます。

getByRole以外にも、findByXXXやgetAllByXXX等沢山ありますが、今回はgetByRoleのみを使いました。

詳細は下記公式サイトに記載されています。

https://testing-library.com/docs/queries/about#types-of-queries
  • [ポイントその3] jest-domを使ってテストする

↑で取得した要素をjest-domのexpectの引数にセットし、matherを使ってテストします。

expect(textbox).toHaveValue('キーワード')

matcher一覧は下記jest-dom公式repoに記載されています。 https://github.com/testing-library/jest-dom

今回はtoHaveValuetoBeCheckedを使いました。 簡単に言うと、

toHaveValueはinputのvalueをtestしたい時に使うmatcherで、

toBeCheckedはcheckoxにcheckが入っているかをtestしたい時に使うmatcherです。

unit testを実行してみる

npm run testを叩きます。

npm run test

> test
> jest

info  - Loaded env from /Users/k-ebato/Documents/workspace/chanfuku/next-typescript-blog-with-search/.env
 PASS  __tests__/components/search-box.spec.tsx
  ✓ 検索ワードが入力された状態で表示されること (115 ms)
  ✓ いくつかのcheckboxがcheckedの状態で表示されること (139 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        4.628 s
Ran all test suites.

今回はreactのunit test初体験、ということで環境構築とテストコードの基礎にフォーカスしましたが、

次回はモジュールのモック化(一部のモジュールをmock化してダミーに置き換えることで、テストコードが書きやすくなる)をやってみたいと思います。


Profile picture

React, Vue, TypeScript, Node.js, PHP, Laravel, AWS, Firebase, Docker, GitHub Actions, etc...