Vite+React+TypeScriptに テスト環境 Jest+TesttingLibrary をステップbyステップで作る
前回 React の テスト方針を調べたでテスト環境について調べました。
今回は、Vite + React + TypeScript + EsLint + Prettier に
テスト環境を Jest + Testting Library を ステップbyステップで作っていきます。
環境作成
まずは Viteを使って、React + TypeScript を作成し、EsLint + Prettier を導入した状態までのプロジェクトを用意します。
環境作成の詳細
環境作成用のコマンド
Viteを使用してReact(TypeScript)アプリを、「testsample-app」というアプリ名で作成します。
npm init vite testsample-app -- --template react-ts
cd testsample-app
npm install
npm install eslint --save-dev
npx eslint --init
// eslint 設定値
? How would you like to use ESLint? (Use arrow keys)
> To check syntax, find problems, and enforce code style
? What type of modules does your project use? (Use arrow keys)
> None of these
? Which framework does your project use?
> React
? Does your project use TypeScript?
> Yes
? Where does your code run?
> √ Browser
? How would you like to define a style for your project? (Use arrow keys)
> Use a popular style guide
? Which style guide do you want to follow? (Use arrow keys)
> Airbnb: https://github.com/airbnb/javascript
? What format do you want your config file to be in? (Use arrow keys)
> YAML
? Would you like to install them now with npm?
> Yes
npm install eslint-config-airbnb-typescript --save-dev
npm install eslint-import-resolver-typescript --save-dev
npm install prettier --save-dev
npm install eslint-config-prettier --save-dev
code .
.eslintrc.ymlファイルを修正
rulesはお好みで変更してください。
env:
browser: true
es2021: true
parser: '@typescript-eslint/parser'
parserOptions:
ecmaFeatures:
jsx: true
ecmaVersion: latest
project: ./tsconfig.json
plugins:
- react
- react-hooks
- '@typescript-eslint'
extends:
- plugin:react/recommended
- plugin:react-hooks/recommended
- airbnb
- airbnb-typescript
- prettier
ignorePatterns:
- vite.config.ts
rules:
# Reactのインポートをチェックしない
react/react-in-jsx-scope: off
# セミコロンつけない
semi:
- error
- never
# デフォルトエクスポートをエラーにする
import/prefer-default-export: off
import/no-default-export: error
.prettierrc.ymlファイルを作成する
こちらもお好みで変更してください。
tabWidth: 2
singleQuote: true
trailingComma: 'none'
semi: false
useTabs: false
package.jsonファイルのscriptタグに追加
"scripts": {
・・・中略・・・
"lint": "eslint --ext .tsx,.ts src/",
"lintfix": "eslint --fix --ext .tsx,.ts src/",
"format": "prettier --write \"**/*.+(js|json|yml|ts|tsx)\""
},
VsCodeの拡張機能、EsLint、Prettier をインストールする。
npm run lint
を行い、エラー箇所(上記の設定の場合は2か所)を修正する。
- <button type="button" onClick={() => setCount((count) => count + 1)}>
+ <button type="button" onClick={() => setCount((precount) => precount + 1)}>
ESLintでDefault Exportをエラーにしているので、App.tsxとmain.tsxがエラーになります。Named Export に修正します。
- function App() {
+ export function App() {
const [count, setCount] = useState(0)
・・・中略・・・
}
- export default App
- import App from './App'
+ import { App } from './App'
テスト環境を作成
「create-react-app」でプロジェクトを作成した場合、下記のものはデフォルトでインストールされています。
@types/jest
@testing-library/react
@testing-library/jest-dom
@testing-library/user-event
Viteで作成した場合は何も入っていませんので一からインストールしていきます。
ステップbyステップでインストールしていきますが、最後の「まとめ」に、今回インストールしたものとその設定値をまとめて載せています。
バージョン情報です。
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@testing-library/react-hooks": "^7.0.2",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.4.0",
"@types/react": "^17.0.33",
"@types/react-dom": "^17.0.10",
"@typescript-eslint/eslint-plugin": "^5.12.0",
"@typescript-eslint/parser": "^5.12.0",
"@vitejs/plugin-react": "^1.0.7",
"eslint": "^8.9.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^16.1.0",
"eslint-config-prettier": "^8.3.0",
"eslint-import-resolver-typescript": "^2.5.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jest": "^26.1.1",
"eslint-plugin-jest-dom": "^4.0.1",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-testing-library": "^5.0.5",
"jest": "^27.5.1",
"prettier": "^2.5.1",
"ts-jest": "^27.1.3",
"typescript": "^4.5.4",
"vite": "^2.8.0"
}
Jest関係のインストール
まずは Jest のインストールからです。
参考にしたのはコチラ:https://www.robinwieruch.de/react-testing-jest/
Jest 本体、TypeScriptを使用しているのでJestの型定義である @types/jest をインストールします。
また、Jest はテストの実行時にテストコードの型検査を行いません。
テストコードの型検査を行うため TypeScript から JavaScript へ変換するためのトランスパイラ ts-jest をインストールします。
※検索すると ts-jest より esbuild-jest のほうが早いなどの情報がありますが、esbuild-jest は開発がストップしているため、jest は v27 で大幅な変更が入ったようですがその変更に対応していないようです。(2022/02/19時点の Jest バージョンはv27.5.1)
npm install jest --save-dev
npm install @types/jest --save-dev
npm install ts-jest --save-dev
Jest設定ファイルの作成
つづいて Jest 設定ファイル「jest.config.json」を作成します。
roots 設定で、Jest がファイルを検索するフォルダを指定します。
testMatch 設定で、テストするファイルを正規表現で指定しています。
transform 設定で、ts/tsx ファイルに対して ts-jest を使うように指定します。
{
"roots": [
"<rootDir>/src"
],
"testMatch": [
"**/__tests__/**/*.+(ts|tsx|js)",
"**/?(*.)+(spec|test).+(ts|tsx|js)"
],
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
}
}
package.jsonを設定
package.json の scriptsに "test" と "test:watch" を追加します。
watch の指定は、ソースコード変更するたびにテストが実行されるようにするものです。
"scripts": {
・・・中略・・・
"test": "jest --config ./jest.config.json",
"test:watch": "npm run test -- --watch"
},
動作確認
動作確認として関数テストを作成してみます。
src/myFunc.tsにmyFunc関数を定義します。
export function myFunc(a:number, b:number) {
return a + b
}
src/myFunc.test.tsにテストを作成します。
import { myFunc } from './myFunc'
test('adds 1 + 2 to equal 3', () => {
expect(myFunc(1, 2)).toBe(3)
})
describe('true is truthy and false is falsy', () => {
test('true is truthy', () => {
expect(true).toBe(true)
})
test('false is falsy', () => {
expect(false).toBe(false)
})
})
testコマンドを実行してみます。
npm run test
テストにパスしました。
Testting library関係のインストール
つづいてTestting libraryをインストールしていきます。
- @testing-library/react
React コンポーネントテストのためのユーティリティライブラリーです。 - @testing-library/jest-dom
Jest のマッチャーを拡張するユーティリティライブラリーです。 - @testing-library/user-event
実際のイベントをシミュレートするためのユーティリティライブラリーです。 - @testing-library/react-hooks
React が提供する Hooks テストのためのユーティリティライブラリーです。
npm install @testing-library/react --save-dev
npm install @testing-library/jest-dom --save-dev
npm install @testing-library/user-event --save-dev
npm install @testing-library/react-hooks --save-dev
動作確認
動作確認としてコンポーネントのテストを作成してみます。
src/MyComponent.tsxにMyComponentコンポーネントを定義します。
export function MyComponent() {
const title = 'Hello Test'
return (
<div>
<p>{title}</p>
</div>
)
}
src/MyComponent.test.tsにテストを作成します。
ドキュメント内に「Hello Test」が含まれているかをテストします。
import { render, screen } from '@testing-library/react'
import { MyComponent } from './MyComponent'
test('「Hello Test」が描画されている', () => {
render(<MyComponent />)
screen.debug()
expect(screen.getByText('Hello Test')).toBeInTheDocument()
})
testコマンドを実行してみます。
npm run test
エラーが発生しました。😢
The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/configuration#testenvironment-string.
Consider using the "jsdom" test environment.
Jest v27 から testEnvironment の仕様が変わり、デフォルト値が "jsdom" から "node "に変更になりました。
DOM 環境をテストする場合は testEnvironment 設定に "jsdom" を指定する必要があります。
参考:Jest 27: Jest の新しいデフォルト設定 - 2021 年版
jest.config.json に testEnvironment 設定を追加します。
{
"roots": [
"<rootDir>/src"
],
"testMatch": [
"**/__tests__/**/*.+(ts|tsx|js)",
"**/?(*.)+(spec|test).+(ts|tsx|js)"
],
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
},
+ "testEnvironment": "jsdom"
}
再度 testコマンドを実行してみます。
npm run test
エラーが発生しているようです。😵
TypeError: expect(...).toBeInTheDocument is not a function
@testing-library/jest-dom usageにちゃんと書いていますね。
テストファイルに毎回 import '@testing-library/jest-dom'
を書くか、設定ファイルをつくって tsconfig で読み込むといいようです。
テストファイルに毎回 import を書くのは嫌なので、設定ファイルを作ります。
jest.setup.ts ファイルを作成します。
jest.setup.ts は EsLint が怒ってきますが今は無視しておきます。
import '@testing-library/jest-dom/extend-expect'
jest.config.json の setupFilesAfterEnv に先ほどのファイルを設定します。
setupFilesAfterEnv 設定には、各テストファイルが実行される直前に実行したいスクリプトファイルを指定します。
{
"roots": [
"<rootDir>/src"
],
"testMatch": [
"**/__tests__/**/*.+(ts|tsx|js)",
"**/?(*.)+(spec|test).+(ts|tsx|js)"
],
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
},
"testEnvironment": "jsdom",
+ "setupFilesAfterEnv": ["<rootDir>/jest.setup.ts"]
}
tsconfig.tsで読み込みます。
{
"compilerOptions": {
・・・略・・・
},
- "include": ["src"],
+ "include": ["src", "jest.setup.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}
再度 test コマンドを実行してみます。
npm run test
テストに成功しました。
EsLintの設定
Jest 用の EsLint のプラグイン、jest-dom 用の EsLint プラグイン、testing-library 用のEsLint プラグインをインストールしておきます。
npm install eslint-plugin-jest --save-dev
npm install eslint-plugin-jest-dom --save-dev
npm install eslint-plugin-testing-library --save-dev
.eslintrc.ymlに設定値を加えます。
plugins に、jest、jest-dom、testing-library を追加します。
extends は、テストファイルにだけ設定値を適用したいので、overrides で指定します。
files で指定するテストファイルは、「Jest設定ファイルの作成」で作成した jest.config.json の testMatch 設定の値を指定します。
env:
browser: true
es2021: true
parser: '@typescript-eslint/parser'
parserOptions:
ecmaFeatures:
jsx: true
ecmaVersion: latest
project: ./tsconfig.json
plugins:
- react
- react-hooks
- '@typescript-eslint'
+ - jest
+ - jest-dom
+ - testing-library
extends:
- plugin:react/recommended
- plugin:react-hooks/recommended
- airbnb
- airbnb-typescript
- prettier
+ overrides:
+ - files:
+ - '**/__tests__/**/*.+(ts|tsx|js)'
+ - '**/?(*.)+(spec|test).+(ts|tsx|js)'
+ extends:
+ - plugin:jest/recommended
+ - plugin:jest-dom/recommended
+ - plugin:testing-library/react
ignorePatterns:
- vite.config.ts
rules:
# Reactのインポートをチェックしない
react/react-in-jsx-scope: off
# セミコロンつけない
semi:
- error
- never
# デフォルトエクスポートをエラーにする
import/prefer-default-export: off
import/no-default-export: error
以上でテスト環境の作成がおわりました。
今回もいろいろとインストールと設定があり、なかなか面倒でした。
まとめ
インストールした内容とその設定をまとめて記載しておきます。
インストール
npm install jest --save-dev
npm install @types/jest --save-dev
npm install ts-jest --save-dev
npm install @testing-library/react --save-dev
npm install @testing-library/jest-dom --save-dev
npm install @testing-library/user-event --save-dev
npm install @testing-library/react-hooks --save-dev
npm install eslint-plugin-jest --save-dev
npm install eslint-plugin-jest-dom --save-dev
npm install eslint-plugin-testing-library --save-dev
設定ファイル
package.jsonに設定追加
"scripts": {
・・・中略・・・
+ "test": "jest --config ./jest.config.json",
+ "test:watch": "npm run test -- --watch"
},
jest.setup.tsを新規作成
import '@testing-library/jest-dom/extend-expect'
jest.config.jsonを新規作成
{
"roots": [
"<rootDir>/src"
],
"testMatch": [
"**/__tests__/**/*.+(ts|tsx|js)",
"**/?(*.)+(spec|test).+(ts|tsx|js)"
],
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
},
"testEnvironment": "jsdom",
"setupFilesAfterEnv": ["<rootDir>/jest.setup.ts"]
}
tsconfig.tsに設定追加
{
"compilerOptions": {
・・・略・・・
},
+ "include": ["src", "jest.setup.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}
.eslintrc.ymlに設定追加
env:
browser: true
es2021: true
parser: '@typescript-eslint/parser'
parserOptions:
ecmaFeatures:
jsx: true
ecmaVersion: latest
project: ./tsconfig.json
plugins:
- react
- react-hooks
- '@typescript-eslint'
+ - jest
+ - jest-dom
+ - testing-library
extends:
- plugin:react/recommended
- plugin:react-hooks/recommended
- airbnb
- airbnb-typescript
- prettier
+ overrides:
+ - files:
+ - '**/__tests__/**/*.+(ts|tsx|js)'
+ - '**/?(*.)+(spec|test).+(ts|tsx|js)'
+ extends:
+ - plugin:jest/recommended
+ - plugin:jest-dom/recommended
+ - plugin:testing-library/react
ignorePatterns:
- vite.config.ts
rules:
# Reactのインポートをチェックしない
react/react-in-jsx-scope: off
# セミコロンつけない
semi:
- error
- never
# デフォルトエクスポートをエラーにする
import/prefer-default-export: off
import/no-default-export: error
追記
2022/05/24 テスト環境 Vitest に関する記事を書きました。
Vite であれば検討する価値はあると思います!
Vite + React + TypeScript に テスト環境 Vitest をステップbyステップで作る
Discussion