はじめに
(今更ですが) Playwright Component Testの存在を最近知りました。
PlaywrigthはE2Eテストライブラリで、NodeJS上でchromium, firefox, webkitのようなブラウザ・レンダリングエンジンでのWebアプリケーションの操作を自動化してくれます。
個人だとAngularをメインで触っていることもあり、Headless Browserの実行はPuppeteer、E2EテストはCypress、のような印象を持っていましたが、React・Vue・SvelteのプロダクトではPlaywrightが採用されるケースを多く見ます。
上記のブログのように、Page(一画面)に対してテストコードを書いて実行する...というのが基本的な使い方ですが、experimentalでComponent Test Runnerも提供されています。
コンポーネントの単体テストにおける課題
私は普段の業務で、コンポーネントの単体テストに対して以下のような課題を感じることがあります。
1. 本来テストするべき内容がブラックボックス化している
以下のようなテストコードを見かけることがあります。
/**
* 例1
*/
it('保存ボタンをクリックしてAPIリクエストに成功したらスナックバーが表示されること。', async () => {
render(<FormComponent />);
// 保存ボタンをクリックする
const submitButtonEl = screen.getByRole('button', { name: 'Submit' });
const user = userEvent.setup();
await user.click(submitButtonEl);
// スナックバーが表示されることを検証する
expect(screen.getByRole('alert')).toHaveTextContent('Success!');
});
/**
* 例2
*/
it('全選択のチェックボックスがクリックされたら全てのチェックボックスが選択状態になること', async () => {
render(<CheckboxGroupComponent />);
// 全選択のチェックボックスを選択する
const selectAllCheckboxEl = screen.getByTestId('select-all');
const user = userEvent.setup();
await user.click(selectAllCheckboxEl);
// コンポーネントのチェックボックスが全て選択されていることを検証する
const checkboxEls = screen.getAllByRole('checkbox');
for (const checkboxEl of checkboxEls) {
expect(checkboxEl).toBeChecked();
}
});
どちらも極端な例なので全テストが上記のようなものというわけではありませんが、もしコードレビューをする立場の方であれば以下のような指摘を行うでしょう。
-
onClick
で何のハンドラが呼ばれているかが不明瞭。引数はどうなっている? - テストあたりの規模が大きすぎる。スナックバーがHooksの副作用処理ならそちらのテストに切り離すべきではないのか?
- 全選択用のチェックボックスをクリックしたらStateが変わるだけであって、他のチェックボックスはそのStateを見ているだけではないのか(matcherが不適切ではないか)
上記のサンプルではコンポーネント表面上の動きを追っているだけであって、処理の妥当性は評価できていないためテストコードとして不適切です。
2. そもそもテストが書けない
以下のようなケースだと、そもそもコンポーネントの品質を単体テストで担保することが困難です。
- viewportのサイズに依存するもの
- Desktop/Mobileに依存するもの
- タイムゾーンに依存するもの
Vitest・Jestのようなテストランナーではブラウザそのものをエミュレートするのは不可能です。そのため、VRT(Visual Regression Testing)や手動テストのような方法に頼らざるを得ないことが多くあります。
Playwright Component Testが解決する課題 + Testing Libraryとの住み分け
良くないテストコードの例を挙げましたが、単体テストとして不適切というだけであって、実際のコンポーネントの動きを追うという観点は重要だと考えています。
事実として結合テストでは似たような操作を手動でも行っているため、その強化材料としてPlaywrightのComponent Testを使えないか?と考えました。
細かい実装を変更する度に手動でポチポチ確認するのもいいですが、Playwright Component Testであればブラウザのエミュレーションが必要なコンポーネントテストもCIで回せばいいでしょう。
また、Vitest(Jest) + Testing LibraryのUnit Testで書いてしまっている中途半端なE2Eテストとの住み分けも可能になり、単体テストのテストコードはビジネスロジックのテストに寄せられるのではないかと考えています。
まとめ
まだ実験的機能ですが、個人的にはこれからガッツリ使っていきたい機能です。
Testing Library + happy-dom(jsdom) のテストコードでは実際のブラウザ上でのUIの振る舞いを検証するものとして不十分なものもあり、手動テストの比重が大きくなることもありました。そこの改善に役立ちそうです。
しかし(それはそれとして)、メソッドレベルの妥当性を評価するUnit Testにおいて、テストコードをE2Eのように(user-eventに依存させて)書いてしまっているといったことは業務でも改善していきたい課題だと捉えています。
「中身の動きが正しいこと」の担保はTesting Library、「実際にブラウザでコンポーネントが正しく動くこと」の担保はPlaywright Component Test、のような切り分け方だとお互いのテストが被らなくていいのかなと思います。
参考