🚀

【Next.js和訳】Testing

10 min read

この記事について

株式会社 UnReact はプロジェクトの一環としてNext.js ドキュメントの和訳を行っています。

この記事は、Testingの記事を和訳したものです。

記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。

Testing

Next.js を、一般的に使用されている 3 つのテストツールでセットアップする方法をご紹介します。CypressJest、そしてReact Testing Libraryです。

Cypress

Cypress は、E2E(End-to-End)テストや統合テストに使用されるテストランナーです。

クイックスタート

create-next-appは、with-cypress の例ですぐに始めることができます。

npx create-next-app --example with-cypress with-cypress-app

手動セットアップ

Cypress を使い始めるには、cypress パッケージをインストールします。

npm install --save-dev cypress

package.json の scripts フィールドに Cypress を追加します。

"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "cypress": "cypress open",
}

Cypress を初めて起動し、Cypress が推奨するフォルダ構造を使ったサンプルを生成する。

npm run cypress

生成されたサンプルと、Cypress ドキュメントのWriting Your First Testセクションを見て、Cypress に慣れるようにしてください。

初めての Cypress 統合テストを作成する

次の 2 つの Next.js ページを想定します。

pages/index.js
import Link from 'next/link'

export default function Home() {
  return (
    <nav>
      <Link href="/about">
        <a>About</a>
      </Link>
    </nav>
  )
}
pages.about.js
export default function About() {
  return (
    <div>
      <h1>About Page</h1>
    </div>
  )
}

ナビゲーションが正しく動作していることを確認するために、テストを追加します。

cypress/integration/app.spec.js
describe('Navigation', () => {
  it('should navigate to the about page', () => {

    // インデックスページからスタートします
    cy.visit('http://localhost:3000/')

    // href属性に "about "が含まれているリンクを探し、クリックします
    cy.get('a[href*="about"]').click()

    // 新しいURLには"/about "が含まれているはずです
    cy.url().should('include', '/about')

    // 新しいページには、"About page "と書かれたh1が含まれているはずです
    cy.get('h1').contains('About Page')
  })
})

cypress.jsonの設定ファイルに "baseUrl":"http://localhost:3000"を追加すると、cy.visit("/")の代わりにcy.visit("http://localhost:3000/")を使うことができます。

Cypress のテストを実行する

Cypress は実際の Next.js アプリケーションをテストしているため、Cypress を起動する前に Next.js サーバーが起動している必要があります。アプリケーションの動作をより忠実に再現するために、本番コードに対してテストを実行することをお勧めします。

npm run buildnpm run startを実行した後、別のターミナルウィンドウでnpm run cypressを実行して Cypress を起動します。

Note
別の方法として、start-server-and-testパッケージをインストールして、package.jsonの scripts フィールドに追加することもできます。"test": "start-server-and-test start http://localhost:3000 cypress" Cypress と連携して Next.js のプロダクションサーバーを起動します。新しい変更を加えた後は、必ずアプリケーションを再構築してください。

継続的インテグレーション(CI)の準備

これまで Cypress を起動すると、インタラクティブなブラウザが表示され、CI 環境には適していないことに気づかれたでしょう。cypress runコマンドを使って、ヘッドレスで Cypress を実行することもできます。

package.js
"scripts": {
  //...
  "cypress": "cypress open",
  "cypress:headless": "cypress run",
  "e2e": "start-server-and-test start http://localhost:3000 cypress",
  "e2e:headless": "start-server-and-test start http://localhost:3000 cypress:headless"
}

Cypress と継続的インテグレーションについては、以下のリソースから詳しく知ることができます。

Jest と React テスティングライブラリ

Jest と React Testing Library は、ユニットテストでよく一緒に使われます。

クイックスタート

Jest と React Testing Library を素早く使い始めるために、with-jest の例create-next-appを使用することができます。

npx create-next-app --example with-jest with-jest-app

手動セットアップ

Jest と React テスティングライブラリを手動でセットアップするには、jest@testing-library/react@testing-library/jest-domのほか、いくつかのサポートパッケージをインストールします。

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

Jest の設定

プロジェクトのルートディレクトリにjave.config.jsファイルを作成し、以下の設定オプションを追加します。

jest.config.js
module.exports = {
  collectCoverageFrom: [
    '**/*.{js,jsx,ts,tsx}',
    '!**/*.d.ts',
    '!**/node_modules/**',
  ],
  moduleNameMapper: {

    /* (CSSモジュールを使った)CSSインポートの処理
    https://jestjs.io/docs/webpack#mocking-css-modules */
    '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',

    // CSSインポートを扱う(CSSモジュールなし)
    '^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js',

    /* 画像の取り込みを扱います
    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: {

    /* babel-jestを使って、next/babelプリセットでテストをトランスパイルする
    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)$',
  ],
}

上記の各オプションについては、Jest のドキュメントで詳しく説明されています。

スタイルシートと画像のインポートの扱い

これらのファイルはテストでは役に立ちませんが、インポートするとエラーになる可能性があるので、モックファイルが必要です。上記の設定で参照したモックファイル(fileMock.jsstyleMock.js)を__mocks__ディレクトリの中に作成します。

__mocks__/fileMock.js
(module.exports = "test-file-stub")
__mocks__/styleMock.js
module.exports = {};

スタティック・アセットの扱いについては、Jest Docsを参照してください。

カスタムマッチャーで Jest を拡張する

@testing-library/jest-domには、.toBeInTheDocument()などの便利なカスタムマッチャーのセットが含まれており、テストを簡単に書くことができます。Jest の設定ファイルに以下のオプションを追加することで、テストごとにカスタムマッチャーをインポートすることができます。

jest.config.js
setupFilesAfterEnv: ['<rootDir>/jest.setup.js']

次に、jest.setup.jsの中に、次のような import を追加します。

jest.setup.js
import '@testing-library/jest-dom/extend-expect'

各テストの前にさらにセットアップオプションを追加する必要がある場合は、上記の jest.setup.js ファイルに追加するのが一般的です。

絶対インポートと Module Path Aliases

Module Path Aliasesを使用しているプロジェクトでは、jsconfig.json ファイルの paths オプションと jest.config.js ファイルの moduleNameMapper オプションをマッチングさせてインポートを解決するように Jest を設定する必要があります。例えば、以下のようになります。

tsconfig.json または jsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "path": {
      "@/components/*": ["components/*"]
    }
  }
}
jest.config.js
moduleNameMapper: {
  '^@/components/(.*)$': '<rootDir>/components/$1',
}

package.json にテストスクリプトを追加する

watch モードの Jest 実行ファイルをpackage.jsonの scripts に追加します。

"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "test": "jest --watch"
}

jest --watch は、ファイルが変更されたときにテストを再実行します。その他の Jest CLI オプションについては、Jest Docsを参照してください。

最初のテストを作成する

あなたのプロジェクトはテストを実行する準備ができました。Jests の慣習に従って、プロジェクトのルートディレクトリにある__tests__フォルダにテストを追加してください。

例えば、<Index />コンポーネントが見出しのレンダリングに成功したかどうかをチェックするテストを追加することができます。

__tests__/index.test.jsx

/**
 * @jest-environment jsdom
 */

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

describe('Home', () => {
  it('renders a heading', () => {
    render(<Home />)

    const heading = screen.getByRole('heading', {
      name: /welcome to next\.js!/i,
    })

    expect(heading).toBeInTheDocument()
  })
})

注意
上記の@jest-environment jsdomのコメントは、テストファイル内のテスト環境をjsdomとして設定しています。これは、React Testing Library がdocument.bodyのような DOM 要素を使用しており、Jest のデフォルトのnodeテスト環境では動作しないためです。また、Jest の設定オプションを追加することで、jsdom環境をグローバルに設定することもできます。jest.config.jsの中の"testEnvironment": "jsdom" を追加します。

オプションで、スナップショットテストを追加して、<Index />コンポーネントへの予期せぬ変更を追跡します。

__tests__/snapshot.js
import React from 'react'
import renderer from 'react-test-renderer'
import Index from '../pages/index'

it('renders homepage unchanged', () => {
  const tree = renderer.create(<Index />).toJSON()
  expect(tree).toMatchSnapshot()
})

Note
テストファイルは pages ディレクトリ内に置かないでください。pages ディレクトリ内のファイルはルートとみなされるからです。

テストスイートの実行

npm run jestを実行して、テストスイートを実行します。テストの合否が決まると、テストを追加する際に役立つインタラクティブな Jest コマンドのリストが表示されます。

さらに読みたい場合は、以下のリソースが役に立つでしょう。

コミュニティのパッケージとサンプル

Next.js のコミュニティでは、パッケージや記事が作成されていますので、参考にしてみてください。

読むべき更なる情報をいかに示しています。

https://nextjs.org/docs/basic-features/environment-variables#test-environment-variablemd

Discussion

ログインするとコメントできます