95
80

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.

Next.js + TypeScriptのプロジェクトにJestを導入する

Last updated at Posted at 2020-04-18

この記事の概要

Next.js + TypeScriptのプロジェクトにJestを導入する手順を解説した記事です。

対象読者

  • Next.jsを触った事がある人
  • TypeScriptの基礎的な知識がある人
  • Jestについて基礎的な知識がある人

この記事を書こうと思った動機

最近Next.jsで個人開発を始めました。

Next.jsはReactベースのフレームワークです。

Reactの create-react-app に似たcreate next-appというコマンドがあります。(プロジェクトのテンプレートを自動生成するツールです)

ただしReactの create-react-app と違ってテスト実行環境は用意されていません。

そこでJESTを導入する手順を残しておきます。

実行環境

  • Node.js 14.15.3
  • Next.js 11.1.2
  • React 17.0.2

具体的な手順とゴール

ここから具体的な手順を記載します。

JestでReactのスナップショットテストを実行出来る環境を構築する事を目標とします。

必要なpackageをインストールする

必要なpackageは以下の通りです。

jest
ts-jest
@testing-library/react
@testing-library/react-hooks
@types/jest
jest-fetch-mock

npm または yarn をインストールします。

npmを使う場合
npm install -D jest ts-jest @testing-library/react @testing-library/react-hooks @types/jest jest-fetch-mock
yarnを使う場合
yarn add jest ts-jest @testing-library/react @testing-library/react-hooks @types/jest jest-fetch-mock --dev

必要な設定ファイルを作成する

以下の3つのファイルを作成します。

test/setupTests.ts
import fetchMock from 'jest-fetch-mock';

fetchMock.enableMocks();
test/tsconfig.jest.json
{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "jsx": "react"
  }
}
jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  roots: ['<rootDir>/src'],
  setupFilesAfterEnv: ['<rootDir>/test/setupTests.ts'],
  testPathIgnorePatterns: ['<rootDir>/.next/', '<rootDir>/node_modules/'],
  transform: {
    '^.+\\.(ts|tsx)$': 'ts-jest',
  },
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
  // https://github.com/zeit/next.js/issues/8663#issue-490553899
  globals: {
    // we must specify a custom tsconfig for tests because we need the typescript transform
    // to transform jsx into js rather than leaving it jsx such as the next build requires. you
    // can see this setting in tsconfig.jest.json -> "jsx": "react"
    'ts-jest': {
      tsconfig: '<rootDir>/test/tsconfig.jest.json',
    },
  },
};

test/tsconfig.jest.jsonjest.config.jsglobals の設定は一見不要ですが、これがないと下記と同様の問題が発生しました。

こちら に対処法が書かれているので参考にさせて頂きました。

テストコード(スナップショットテスト)

テスト対象のComponentは下記の通りです。

src/components/AppTitle.tsx
import React from 'react';
import styled from 'styled-components';

const Title: React.FC = styled.h1`
  color: red;
  font-size: 50px;
`;

const AppTitle: React.FC = () => {
  return <Title>message</Title>;
};

export default AppTitle;

対応するスナップショットテストは下記の通りです。

src/components/__tests__/AppTitle.spec.tsx
/**
 * @jest-environment jsdom
 */
import React from 'react';
// eslint-disable-next-line import/no-extraneous-dependencies
import { render } from '@testing-library/react';
import AppTitle from '../AppTitle';

test('AppTitle', () => {
  const { asFragment } = render(<AppTitle />);
  expect(asFragment()).toMatchSnapshot();
});

テストコード(通常のテストコード)

Reactではない通常のTypeScriptの関数のテストも用意します。

src/domain/Sample.ts
// テスト対象の関数
export const square = (num: number) => {
  return num * 2;
};
src/domain/__tests__/Sample.spec.ts
// Sample.ts squareのテストコード
import { square } from '../Sample';

describe('Sample.ts Functions TestCases', () => {
  it('should return the squared value', () => {
    const result = square(2);
    const expected = 4;

    expect(result).toStrictEqual(expected);
  });
});

テストコード(カスタムフックのテストコード)

以下はGitHubのAPIからリポジトリの一覧を取得するカスタムフックです。

import { useEffect, useState } from 'react';
import { CardListItem } from '../components/CardList';
import { fetchPublicRepos } from '../api/fetch/github';
import { isSuccessResult } from '../domain/asyncResult';

export type UseFetchPublicReposReturnType = {
  items?: CardListItem[];
};

const useFetchPublicRepos = (): UseFetchPublicReposReturnType => {
  const [items, setItems] = useState<CardListItem[]>();

  useEffect(() => {
    const fetchRepo = async () => {
      const publicRepos = await fetchPublicRepos();
      if (isSuccessResult(publicRepos)) {
        const cardListItems = publicRepos.value.map((gitHubRepository) => ({
          id: gitHubRepository.id,
          title: gitHubRepository.name,
          url: gitHubRepository.htmlUrl,
          description: gitHubRepository.description,
        }));

        setItems(cardListItems);
      }
    };

    // eslint-disable-next-line @typescript-eslint/no-floating-promises
    fetchRepo();
  }, []);

  return { items };
};

export default useFetchPublicRepos;

以下のようにテストコードを実装します。

/**
 * @jest-environment jsdom
 */
import fetch from 'jest-fetch-mock';
import { renderHook, act } from '@testing-library/react-hooks';
import useFetchPublicRepos from '../useFetchPublicRepos';

describe('useFetchPublicRepos TestCases', () => {
  beforeEach(() => {
    fetch.resetMocks();
  });

  it('should be able to fetch public GitHub Repositories', async () => {
    const mockBody = [
      {
        id: 1,
        name: 'my-terraform',
        html_url: 'https://github.com/keitakn/my-terraform',
        description: '個人の検証用で使うTerraform',
      },
    ];

    const mockParams = {
      status: 200,
      statusText: 'OK',
    };

    fetch.mockResponseOnce(JSON.stringify(mockBody), mockParams);

    const expected = {
      items: [
        {
          id: 1,
          title: 'my-terraform',
          url: 'https://github.com/keitakn/my-terraform',
          description: '個人の検証用で使うTerraform',
        },
      ],
    };

    await act(async () => {
      const renderHookResult = renderHook(() => useFetchPublicRepos());

      await renderHookResult.waitFor(() => {
        expect(renderHookResult.result.current).toStrictEqual(expected);
      });
    });
  });
});

テストの実行

package.jsonscripts に以下を定義します。

package.json
{
  "scripts": {
    "test": "jest",
    "test:coverage": "jest --collect-coverage"
  }
}

yarn run test もしくは npm run test を実行します。

以下のように表示されればテストが成功しています。

$ jest
 PASS  src/domain/__tests__/Sample.spec.ts
 PASS  src/components/__tests__/AppTitle.spec.tsx

Test Suites: 2 passed, 2 total
Tests:       2 passed, 2 total
Snapshots:   1 passed, 1 total
Time:        1.628s, estimated 3s
Ran all test suites.
✨  Done in 2.23s.

src/components/__tests__/AppTitle.spec.tsx はスナップショットテストです。

初回はスナップショットファイルが生成されますので、これをバージョン管理に含めておきます。
これによってUIの意図しない防ぎます。

(参考) Testing React Apps スナップショットテスト

おわりに

今回作成したテストやComponentはかなり単純な物なので、開発が進むにつれ、設定等を追加する事になると思います。

その場合はまた何らかの形で情報発信を行います。

以上になります。最後まで読んで頂きありがとうございます。

95
80
2

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
95
80

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?