Open22

WordPressのカスタムブロックのユニットテストやってみる

ちあきちあき

なんか情報あるかなーってざっくり調べてみたけど、それっぽい情報が5年とか6年とか前なのでさすがに今はなんかありそう・・・?

ちあきちあき

今はJest + Testing Libraryでやれるみたい

Tests for JavaScript use Jest as the test runner and its API for globals (describe, test, beforeEach and so on) assertions, mocks, spies and mock functions. If needed, you can also use React Testing Library for React component testing.

ちあきちあき

一旦小難しいことは考えずに、 npx @wordpress/create-block@latest で作ったブロックをテストしてみる。

ちあきちあき

ブロックを配置すると、こんな感じで定型文言のブロックが配置されます。

ちあきちあき

さすがに定型文出すだけだとテストとは?ってなるから入力値が変えれるようにしようかな

ちあきちあき

ついでにtsに変えるか・・・(こうして本筋から逸れていく)

ちあきちあき
export default function Edit( {
	attributes: { message },
	setAttributes,
}: BlockEditProps< BlockAttributes > ) { ... } );

みたいに変える

ちあきちあき

テストファイルは test ディレクトリに入れる

ちあきちあき

Prettierないのイライラしてきた!!!!!!!!!!!!!!いれる

ちあきちあき

カスタムブロックそのものの unit test は結構難しいので、

  • コンポーネントに切り分けて単体テスト
  • カスタムブロックそのもののテストは playwright での e2e でやるってケース

が多いって教えてもらった!

ちあきちあき

setAttribute とか useSelect とかがmock 出来ないから、Edit とか Save のコンポーネントそのものを単体テストが出来ないんですよね。(単体テスト = unit test)

ちあきちあき

じゃあ「単体テストを書いてみる」っていう視点だったらどういうコードを書いたらいいんだ?って思ったので一旦ChatGPTに聞いてみる。

ちあきちあき

💡 じゃあ「テストしやすいブロック」って?

以下の特徴を持ってるとJestでの単体テストがしやすくなるよ:

1. 属性の入力 UI を独立したコンポーネントに分ける

例:<ColorPicker />, <ImageSelector />, <TableBuilder /> など

👉 Edit.js では <ColorPicker value={ color } onChange={ setColor } /> って呼び出すだけにしておくと、ColorPicker自体は普通のReactコンポーネントとしてテストできる!

2. 属性値を変換する処理を util に切り出す

例:

// utils/transform.js
export function getTableHTMLFromState(state) { ... }
export function getStateFromTableHTML(html) { ... }

👉 ここは完全にユニットテストの世界。I/Oが明確なのでテストしやすい!

3. 入力 → 表示が直結する軽量な構成にしておく(練習用)

最初の練習としておすすめなのは:

✨「属性付きメッセージブロック」

機能 説明
message 属性 入力欄に入力した文字を表示
type 属性 "info", "warning", "error" の切り替え(クラス名変わる)

こうすると:

  • EditMessageBlock コンポーネント:Jestでテスト
  • getClassNameByType(type) みたいな関数:ユーティリティのユニットテスト
  • ブロック全体の流れ:PlaywrightでE2E

という分割ができる!

ちあきちあき

jest.config.jsonもtsに対応させる。ts-jestも必要。

npm install --save-dev ts-jest
jest.config.json
{
	"preset": "@wordpress/jest-preset-default",
	"transform": {
		"^.+\\.(ts|tsx)$": "ts-jest"
	},
	"moduleFileExtensions": [ "js", "ts", "tsx", "json", "node" ],
	"testEnvironment": "jsdom",
	"testMatch": [ "**/test/**/*.test.(ts|tsx|js)" ]
}