はじめに
Clean Architecture 達人に学ぶソフトウェアの構造と設計を読んだので、これを参考にテスタブルなReactプロジェクトを作ろうと思いました。
ディレクトリ構成について考えがまとまり、いざ作ろう!となった段階で、開発環境の構築で苦労してしまいました。
備忘録的な感じで、まとめておきます。
目次
- 環境構築の手順
- React, TypeScript, Viteでのアプリケーション作成
- ESLint, Prettierのセットアップ
- Jestのセットアップ
- GitHub Actionsのセットアップ
環境構築の手順
React, TypeScript, Viteでのアプリケーション作成
以下のコマンドを実行するだけで、簡単にプロジェクトが作成できます。
$ yarn create vite my-react-ts-app --template react-ts
参考
ESLint, Prettierのセットアップ
次に、ESLintとPrettierのセットアップをしていきます。
ESLint
まずはESLintのセットアップからです。最初に以下のコマンドを実行します。
$ yarn add eslint --dev
$ yarn create @eslint/config
すると、質問がいくつか出てくるので、以下のように答えます。
✔ How would you like to use ESLint? · problems
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · react
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ What format do you want your config file to be in? · JavaScript
The config that you've selected requires the following dependencies:
eslint-plugin-react@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
✔ Would you like to install them now? · No
すると以下のESLint用設定ファイルが作成されます。
module.exports = {
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"react",
"@typescript-eslint"
],
"rules": {
}
}
nodeの環境でも実行するので以下の変更を加えてください。
env: {
browser: true,
es2021: true,
+ node: true
},
パッケージマネージャに yarn
を使う想定で書いています。以下のコマンドを実行してください。(実は質問の中で npm
を使ってinstallしますか?という質問が存在します。もし npm
を使っている場合は、そこでyesを選択すると、以下のコマンドは実行しなくても大丈夫です。 )
$ yarn add --save-dev eslint-plugin-react@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
これで一通りのESLintの設定は終わりです。
今回はここで、ReactやReact hooksに対するESLint設定も加えておきましょう。
$ yarn add --dev eslint-plugin-react-hooks
.eslintrc.js
に以下の変更を加えてください。
- plugins: ['react', '@typescript-eslint'],
+ plugins: ['react', 'react-hooks', '@typescript-eslint'],
rules: {
+ 'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
+ 'react-hooks/exhaustive-deps': 'warn', // Checks effect dependencies
+ 'react/jsx-key': [
+ 'error',
+ {checkFragmentShorthand: true, warnOnDuplicates: true},
+ ],
この設定を入れることで、Reactでのアプリ作成中に気にしなければいけないルールをESLintが検知してくれるようになります。
ルールに関しては、ドキュメントを添付するので見てみてください!
参考↓
Prettier
次にPrettierの設定をしていきます。
$ yarn add --dev --exact prettier
$ echo {}> .prettierrc.json
$ touch .prettierignore
Prettierによってコード整形しなくて良いファイルは .prettierignore
に記述しておきましょう。
# Ignore artifacts:
build
coverage
# Ignore all HTML files:
*.html
また、ESLintとPrettierのルール同士が競合してお互いの動作を邪魔することがあります。その競合を抑えるために、 eslint-config-prettier
をinstallして、 .eslintrc.js
に変更を加えます。
$ yarn add --dev eslint-config-prettier
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:promise/recommended',
+ 'prettier',
],
ではコードの整形を行ってみましょう。
$ yarn prettier --write .
コードが全てのファイルで一定のルールに従って、統一できたかと思います。個人開発用のプロジェクトだったとしても、統一しておくと読みやすいため、Prettierの設定を入れることをお勧めします。
参考
おまけ
husky
, lint-staged
による自動ESLintチェックの設定もついでにご紹介します。以下のコマンドを実行してください。
$ npx mrm@2 lint-staged
そして、package.json
に変更を加えます。
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
+ "lint": "eslint .",
~~
"vite": "^2.9.5"
+ },
+ "lint-staged": {
+ "*.{js,ts,tsx}": "yarn lint"
}
}
これで、commit時に勝手にESLintによる静的チェックが入るようになります。もしルールに沿っていなければ、勝手にチェックしてくれるので、知らないうちにバグが混入していた、ということを防げる可能性が上がりました。
参考
Jestのセットアップ
次はJestのセットアップです。後少しです。頑張りましょう!
まずは以下のコマンドを実行して、必要なパッケージをインストールしましょう。
$ yarn add --dev jest @types/jest ts-jest ts-node
※ 注意
jest, ts-jestのメジャーバージョンを揃える必要があります。筆者が試した時は、28がまだリリースされて間もない、ということもあり、27で揃えました。
次に、設定を行います。以下のコマンドを実行してください。
$ jest --init
すると以下のような質問が表示されるので、答えてください。
✔ Would you like to use Jest when running "test" script in "package.json"? … yes
✔ Would you like to use Typescript for the configuration file? … yes
✔ Choose the test environment that will be used for testing › node
✔ Do you want Jest to add coverage reports? … no
✔ Which provider should be used to instrument code for coverage? › v8
✔ Automatically clear mock calls, instances, contexts and results before every test? … yes
すると、 jest.config.ts
が出来上がります。少し設定を書き換えます。
import {InitialOptions} from '@jest/types/build/Config';
const config: InitialOptions = {
clearMocks: true,
// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
statements: 100,
},
},
// A list of paths to directories that Jest should use to search for files in
roots: ['<rootDir>/src'],
// The glob patterns Jest uses to detect test files
testMatch: [
'**/__tests__/**/*.+(ts|tsx|js)',
'**/?(*.)+(spec|test).+(ts|tsx|js)',
],
// A map from regular expressions to paths to transformers
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest',
},
};
export default config;
まずデフォルトで作成される jest.config.ts
は型が指定されていないため、設定ファイルに変なプロパティが紛れ込む可能性があります。なので、 InitialOptions
で型を指定してあげましょう。
また、今回は C0 = 100%
を基準とするため、 coverageThreshold
を設定しています。
それ以外は、基本的に TypeScript Deep Dive を参考にします。
設定はこれで完了です。
最後に、簡単なテストの実行してみましょう。
export const sum
= (...a: number[]) =>
a.reduce((acc, val) => acc + val, 0);
import { sum } from './foo';
test('basic', () => {
expect(sum()).toBe(0);
});
test('basic again', () => {
expect(sum(1, 2)).toBe(3);
});
$ yarn test
これでテストが実行され、完了します。
※ 注意
jest --init
のコマンド実行時に package.json
に自動でscriptに "test": "jest"
が追加されているはずです。ただし、追加されていない場合は、追加してから上記のコマンドを叩いてみてください。
次に、Reactのテストを行いたい場合の環境を作っていきます。必要なモジュールをインストールしていきます。
$ yarn add --dev @testing-library/react @testing-library/jest-dom
実際のテストは書きません(別記事で書きたいと思います)が、テストを行う際は以下のように作ります。
/**
* @jest-environment jsdom
*/
import React from 'react';
import {render, screen} from '@testing-library/react';
import '@testing-library/jest-dom';
describe('テストの内容', () => {
~~~
});
順に説明していきます。
/**
* @jest-environment jsdom
*/
テストに使用する環境をブラウザ環境にする、という宣言です。デフォルトではNode.js環境で行うため、Reactのコンポーネントやhooksのテストを行う場合には、テストの最上部にこちらの宣言が必要になります。
import React from 'react';
import {render, screen} from '@testing-library/react';
Reactのテストに必要なモジュールをimportします。 render
や screen
などの @testing-library/react
からimportできるものは必要に応じて、importしてください。
import '@testing-library/jest-dom';
toHaveTextContent
などマッチャーの拡張を行いたい場合は、上記が必要になります。 @testing-library/jest-dom
をimportすることによって使用できるようになるマッチャーは、
- toBeDisabled
- toBeInTheDocument
- toHaveTextContent
などです。詳しくはドキュメントでご確認いただければと思います。
参考
GitHub Actionsのセットアップ
ようやく最後の段階にきました!GitHub Actionsのセットアップを行います。
まず、CIで実行したいコマンドを定義しましょう。
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
+ "lint": "eslint .",
+ "check": "prettier --check .",
次に設定ファイルを作成します。
$ mkdir -p .github/workflows/
$ touch .github/workflows/on-pull-request.yml
設定を記述していきます。
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: On pull request
on:
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v3
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install Dependencies
run: yarn install --prefer-offline
- name: Run eslint
run: yarn lint
- name: Run prettier
run: yarn run check
- name: Run jest
uses: ArtiomTr/jest-coverage-report-action@v2
with:
package-manager: yarn
skip-step: install
test-script: yarn test
CIでESLintやPrettierによるチェックを行います。
また、テストを実行していますが、実行するだけではなく、coverageも同時にPRへ出力されるようにしたいと思います。
今回は ArtiomTr/jest-coverage-report-action@v2
を使用して、上記を実現します。依存関係のインストールから、テストの実行までをこちらで行ってくれるのですが、依存関係はすでに前段階でインストール済みなので、 skip-step: install
というオプションを使います。他にもいくつも設定ができますので、ドキュメントを参照してみて下さいね。以下にURLを添付しておきます。
参考
まとめ
普段はすでに作成されたプロジェクトを使って開発しているため、こういった環境構築は少し慣れない部分もあり、てこずりました。
本来の目的であったテスタブルなReactプロジェクト作成及びテストコードの指針作成に向けて、今回作成したリポジトリで引き続き開発を続けていきます。
次は、テスタブルなReactプロジェクト及びテストコードについて記事を書けたらと思います。