Closed11
【key-front】react-testing-libraryの素振りをしてみた(前編)
モチベーション
- 毎週金曜日Slackのkey_frontチャンネルでハドル機能を使いお題に対してメンバー同士ディスカッションをする時間を15〜30分程度設けている
- 今回は「react-testing-libraryを触ってみて」の共有会
- ファシリテーターは筆者なので、事前に読み込んで気になった点などをスクラップに投げていく
- 開催日は9/21(木)で最終的に議事録として結論をまとめる
後編
メモ1
- 普段はjest-puppeteerとかCypressのようなe2eテストを書くことが多い(けど有名なテストトロフィーだとe2eの優先度は低い)
- なぜかjest-puppeteerに関するZennの記事が少ないから誰かの助けになるかもと思っていくつか記事を作っていた。
- 実際にe2eをやってるけどクリティカルなところがメインになる。ログインとか購入導線とかそもそも落ちたらまずいよね。というところ。
- 業務委託でreact-testing-libraryを触っているけれど、一旦公式とかに沿って改めてテストコードを書いていきたいと思ってお題にした。
Next.jsの公式ドキュメントにもあるようにテストを試せるテンプレートが用意されているので、これを骨子にいろいろ試していく
npx create-next-app@latest --example with-jest with-jest-app
create-next-app で with-jest のテンプレートを利用すると、Jest と React Testing Library をすぐに使い始めることができます
Next.js 英語
Next.js 日本語
test('loads and displays greeting', async () => {
// Arrange
// Act
// Assert
})
- ArrageにはReact要素をDOMにレンダリングするrenderメソッドを配置する
- Actにはユーザーのアクションをシミュレートするためのイベントを発生させる
- Assertにはテストの結果(期待値)を書く
具体例
// Arrage
render(<Fetch url="/greeting" />)
// Act
fireEvent.click(screen.getByText('Load Greeting'))
await screen.findByRole('alert')
// Assert
expect(screen.getByRole('alert')).toHaveTextContent('Oops, failed to fetch!')
expect(screen.getByRole('button')).not.toBeDisabled()
react-testing-libraryのexampleで登場するmswに関して補足
メモ2
with-jest のテンプレートでもデフォルトで入っているテストコードもArrange、Act、Assertでテストが書かれている
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()
})
})
- it関数やtest関数を1つのファイルに複数記述することができる
- it関数やtest関数を複数含めることができるdescribe関数がある
- describe関数のブロックはTest Suitesと呼ばれる
- test/it関数のブロックは Test(Test Case)と呼ばれる
// テストスイート
describe("true is truthy and false is falsy", () => {
// テストケース
test("true is truthy", () => {
expect(true).toBe(true);
});
// テストケース
it("false is falsy", () => {
expect(false).toBe(false);
});
});
- screen.debug();でデバッグすることができる
- テストで使えるconsole.log();みたい
describe("Home", () => {
it("renders a heading", () => {
render(<Home />);
screen.debug();
const heading = screen.getByRole("heading", {
name: /welcome to next\.js!/i,
});
expect(heading).toBeInTheDocument();
});
});
ざっとreact-testing-libraryと公式ドキュメントやwith-jestテンプレートの中身を見終わったので下記で素振りを続ける
要素取得
- getByText
- queryByText
- findByText
import { render, screen } from "@testing-library/react";
import App from "@/pages/01_sample";
describe("try getByText, queryByText, findByText", () => {
// NOTE:テキストから要素取得
it("try getByText", () => {
render(<App />);
const element = screen.getByText("Hello");
expect(element).toBeInTheDocument();
});
// NOTE:取得できない場合はnullを返す(処理がストップしない)
it("try queryByText", () => {
render(<App />);
const element = screen.queryByText("hey");
expect(element).not.toBeInTheDocument();
});
// NOTE:findByTextの返り値がPromiseなのでasync/awaitが必要になる
it("try renders Hello", async () => {
render(<App />);
const element = await screen.findByText("Hello");
expect(element).toBeInTheDocument();
});
});
- getAllByText
import { render, screen } from "@testing-library/react";
import App from "@/pages/02_sample";
it("try getAllByText", () => {
render(<App />);
// NOTE:配列で取得する(複数取得することができる)ターゲットはhtmlタグではなくテキスト
const elements = screen.getAllByText("Hello");
expect(elements).toHaveLength(2);
});
- getAllByRole
import { render, screen } from "@testing-library/react";
import App from "@/pages/03_sample";
// NOTE: https://www.w3.org/TR/html-aria/#docconformance
it("try getAllByRole", () => {
render(<App />);
// htmlタグ(厳密にはrole)で複数取得する
const listElements = screen.getAllByRole("listitem");
expect(listElements).toHaveLength(3);
});
- getByTestId
import { render, screen } from "@testing-library/react";
import App from "@/pages/04_sample";
it("try getByTestId", () => {
render(<App />);
// NOTE:Cypressのdata-cyと同じ感じ
const element = screen.getByTestId("test");
expect(element).toBeInTheDocument();
});
環境構築
with-jestテンプレートと下記の記事を骨子に環境構築をする
jest
npm install jest --save-dev
npm install @types/jest --save-dev
npm install ts-jest --save-dev
testing-library
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 --save-dev jest-environment-jsdom
React18の場合は@testing-library/react-hooks
はインストールしなくてok(下記参照)
jest.config.json
{
"roots": ["<rootDir>/resources/js"],
"testMatch": [
"**/__tests__/**/*.+(ts|tsx|js)",
"**/?(*.)+(spec|test).+(ts|tsx|js)"
],
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
},
"testEnvironment": "jest-environment-jsdom",
"setupFilesAfterEnv": ["<rootDir>/jest.setup.ts"]
}
jest.setup.ts
// Optional: configure or set up a testing framework before each test.
// If you delete this file, remove `setupFilesAfterEnv` from `jest.config.js`
// Used for __tests__/testing-library.js
// Learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom'
stylelint.json
"ignoreFiles": ["vite.config.ts", "jest.setup.ts"], // jest.setup.tsを追加
tsconfig.json
{
"compilerOptions": {
"types": ["react", "vite/client", "jest"] // jestを追加
},
"include": ["resources/js/**/*", "jest.setup.ts"], // jest.setup.tsを追加
}
package.jsonに追加
"test": "jest",
"test:watch": "jest --watch",
"test:ci": "jest --ci"
lint-stagedはstagingに上がっているファイルが対象になるのでpre-commitで実行する。コミット毎にtest実行するとスピード感持って実装できなくなるのでpre-pushのタイミングでtestを実行する。本当はCIに組み込むのがベター。
.husky/pre-push
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run test
メモ
買ったが3割くらいしか消化できていない。今度読み切って別途全体に共有する
下記もフロントエンドのテストでバズってた記事。積読だったので後で見る
議事録_20230921
- 9/21(木)に実施
- 結論を出すコンテンツではなかったため特に結論はなし
- 参加人数は6名(以下エビデンス)
このスクラップは2023/09/21にクローズされました