Next.jsにJestとReact Testing Libraryを追加してコンポーネントをテストする

Next.jsにJestとReact Testing Libraryを追加してコンポーネントをテストする

2021/09/26

DEV

仕事でReact Testing Libraryでコンポーネントテストをよく書くので、趣味の個人開発品でも導入してみることにしてみました。

調べてみると、今年8月にNext.js公式DocsにTestingが追加されているではありませんか。
https://nextjs.org/docs/testing
しかも、Jest + React Testing Libraryというやりたかった構成。
仕事ではJestでなくmochaを使っているので、Jestデビューすることにしました。(大して変わらんだろう。?)

基本はNext.jsのDocsの手順を追っているだけなので詳しくはそちらを見てほしいですが、ところどころ不要な設定は省いているので、とりあえず最短で動かしてみたい方に役に立つかもです。

バージョン

React 17.0.2
Next.js 11.1.2

必要パッケージをインストール

// bash
npm install --save-dev jest babel-jest @testing-library/react @testing-library/jest-dom identity-obj-proxy react-test-renderer

Jestの設定

ルートディレクトリにjest.config.js を作って、以下の内容を書き写します。
(Next.jsのDocsコピペです。。詳細はJest docs参照とのこと。

// jest.config.js
module.exports = {
  collectCoverageFrom: [
    '**/*.{js,jsx,ts,tsx}',
    '!**/*.d.ts',
    '!**/node_modules/**'
  ],
  moduleNameMapper: {
    /* Handle CSS imports (with CSS modules)
    https://jestjs.io/docs/webpack#mocking-css-modules */
    '^.+\\.module\\.(css|sass|scss)$''identity-obj-proxy',


    // Handle CSS imports (without CSS modules)
    '^.+\\.(css|sass|scss)$''<rootDir>/__mocks__/styleMock.js',


    /* Handle image imports
    https://jestjs.io/docs/webpack#handling-static-assets */
    '^.+\\.(jpg|jpeg|png|gif|webp|svg)$''<rootDir>/__mocks__/fileMock.js'
  },
  testPathIgnorePatterns: ['<rootDir>/node_modules/''<rootDir>/.next/'],
  testEnvironment: 'jsdom',
  transform: {
    /* Use babel-jest to transpile tests with the next/babel preset
    https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object */
    '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }]
  },
  transformIgnorePatterns: ['/node_modules/''^.+\\.module\\.(css|sass|scss)$'],
};

npmスクリプトにテストコマンドを追加

// package.json
"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "test": "jest"
}

準備完了😇テストを書く

src/pages直下のindex.tsxでh1タグが正常に描画されるかどうかの簡単なテストを行います。

// index.tsx

import React from 'react';

export default function Home() {
  return (
    <div className="container">
      <h1>My Dashboard</h1>
    </div>
  );
}

同じディレクトリにindex.test.tsxを作成します。

import { render, screen } from '@testing-library/react';
import React from 'react';
import Home from './index';

describe('Home'() => {
  // h1タグのテキストが表示されることを検証
  it('should render the h1'() => {
  // コンポーネントをレンダリング
    render(<Home />);
  // queryByTextは存在しない場合nullを返す。※getByTextの場合存在しないとエラーになるので注意
    const h1 = screen.queryByText('My Dashboard');
  // h1がいることを検証
    expect(h1).toBeTruthy();
  });
});

テストを実行

// bash
npm run test

> my-dashboard@0.1.0 test C:\Users\shuhe\OneDrive\ドキュメント\code\my-dashboard
> jest


 PASS  src/pages/index.test.tsx
  HomePage
    √ should render the heading (32 ms)


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

テスト成功!

React Testing Libraryは、ユーザーの動作を模擬して色々パターン分けしてUIのテストができるので、機能追加の際などもテストが走ってくれていると安心感ありますね。
Jestのマッチャー関数が色々ありそうなので、どんどん試してみようと思います。

追加情報

公式Docsでは他の手段やもう少し便利に動かす方法もあるので念のため紹介します。

  • Cypressを使った方法も解説されています。自分はあまりなじみがないですがコード見る感じ直感的で面白そう。
  • コンポーネント内でcssモジュールやファイルをimportしている場合エラーになってしまうため、それらの為のmockファイルを作成します。(した方が良いです)
// __mocks__/fileMock.js
(module.exports = "test-file-stub")

// __mocks__/styleMock.js
module.exports = {};
  • 初めにimportした@testing-library/jest-domを用いるとjest-domの持つ便利なマッチャー関数(.toBeInTheDocument()など)を用いることができますが、いちいち呼び出し不要で使えるように、jest.config.jsに以下セットアップファイルの呼び出しを追加し、ルートディレクトリにjest.setup.jsを作成し、jest-domの拡張をimportします。これで、カスタムマッチャー関数がどこでも使えるようになります。
// jest.config.js
setupFilesAfterEnv: ['<rootDir>/jest.setup.js']

// jest.setup.js
import '@testing-library/jest-dom/extend-expect'
  • プロジェクト内でModule Path Aliase(@/pages/~~的にpathを呼べるようにする設定)を用いている場合はそのtest側にも適用しないといけません。
// tsconfig.json or jsconfig.jsonにて以下のように設定している場合
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/components/*": ["components/*"]
    }
  }
}

// jest.config.jsに以下を追加
moduleNameMapper: {
  '^@/components/(.*)$': '<rootDir>/components/$1',
}
  • npmスクリプトのテストコマンドを"test": "jest"から"test": "jest --watch"とすると、修正毎にテストが走ってくれます!(僕は使いません!)