フロントエンドのテストに関するZenn記事を漁る
ほぼKentさんの記事で読んだことある内容だった
「テストはネストしないほうがいいよ」という話だけ初耳だったけど、グルーピング化した方がいい場面はないのだろうか?ちゃんと検討したことがないのでまだわからない
また、テストを正確に書きやすくするために、以下のeslintのプラグインを追加すると良いと思います。これはオプションです。
$ yarn add -D eslint-plugin-jest-dom
$ yarn add -D eslint-plugin-testing-library
最後に、状態管理やUIライブラリのProviderをラップしておいてくれるように、@testing-library/reactのrenderをカスタムしたものを用意しておくと、テストを書くのが少し楽になります。
import { FC, ReactElement } from 'react';
import { render, RenderOptions, RenderResult } from '@testing-library/react';
import { Provider, store, ThemeProvider, theme } from './providers';
const AllTheProviders: FC = ({ children }) => (
<Provider store={store}>
<ThemeProvider theme={theme}>
{children}
</ThemeProvider>
</Provider>
);
const customRender = (ui: ReactElement, options?: Omit<RenderOptions, 'wrapper'>): RenderResult =>
render(ui, { wrapper: AllTheProviders, ...options });
export * from '@testing-library/react';
export { customRender as render };
先ほど書いたようにgetByはgetできなかったときはエラーを吐くため、getByを使うとscreen.getByText(/[0-9]文字以上で入力してください/)の部分ですでにテストが失敗するため、エラーメッセージが無いということが確かめられません。
そのため、取得する要素が存在しなくてもエラーを吐かないqueryByの方を使っています。
testing-library-selector
というライブラリでこういう書き方ができるみたい
import { byRole } from 'testing-library-selector';
const ui = {
loginButton: byRole('button', { name: /ログイン/ }),
};
...
expect(ui.loginButton.get()).toBeDisabled();
const spy = jest.spyOn(console, 'error'); spy.mockImplementation(() => {}); ... spy.mockRestore();
この3行はコンソールへのわざと出したエラーの出力を省き、テストの結果を読みやすくしています。
コードの詳細な意味がわからなかったので要確認
jest-in-case
て、ネーミングうますぎでしょ
自分の経験的には、ユニットとテストの最初の一つを書いたらあとは自然とその周辺で増えていく。サンプルがあったら人はコピペする。逆にいうと最初の一つを書かない限り一切書かれない。まず一つ用意するのが大事。
mizchiさんの開発者に関する洞察かなり好きだ
...プログラマにとってはユニットテストの方が「安く、速く、うまい」。
ユニットテストは E2E テストと独立しているが、間接的に E2E を通すために下支えとなる。責務を分割できない実装すると、自然に E2E が増えてしまう。つまり、理論的にはモジュールごとに正しい責務を分割できていれば、本来の目的である E2E テストは最小限で済むはずである。
KentさんもE2Eはhappy pathをカバーするために使えって書いてたな
今回は vitest を使う。mocha, ava, jest と今まで使った中で一番体験がいい。 API が jest 互換なので、移行しやすい。今の jest は commonjs/ESM の混乱が直撃してしまっており、正直設定が面倒になってしまっている。vitest はある程度丸投げできる。
Storybookではvitestまだうまく扱えなかった気がする
このとき、 tests や test/** ではなく、実装したいディレクトリで実装してしまってよい。
異論はあると思うが、テストが見えないディレクトリに隠蔽されていることで、見た目上きれいになっても、維持する対象という意識が薄れて、テスト意識が低い人に放置される害のが大きいと感じている。テストは消極的な存在ではなく、積極的に混ぜていったほうが逆にいいと考えている。
自分もこれだな
ことReactコンポーネントのテストに関してはscaffdogで雛形を自動生成して云々カンヌン
このとき、describe や context を積極的には使わない。ファイル名自体がテストのためのスコープを持つので、基本的にはそれで十分とする。 describe があるときは、専用の beforeEach 等があることを期待する。
なるほど
ここで意識することとして、expect() のアサーションも .toBe() や .equal() のような単純なものしか使わないようにしている。
地味に大事かもしれない
jest/vitest は NODE_ENV に test が入っているので、ランタイムで実行されないように分岐を入れてテストを書く。コード内にプライベートな関数やモジュールが多いとき、一旦こう書いてしまう。
なるほど
うーん、シンプルに「満たすべき仕様(ユースケース)をテストとして記述し、最終的にはそれがpassされていることを目指す」「最初にテストを書くことで明確にゴールが意識されていて生産性を保ちやすい」みたいな話じゃないのかな
これができるには「実装のスコープがはっきりしていて、かつそんなに大きい粒度でない」条件が必要な気がした。例えばプロジェクトの最初からTDDやるってなったときに、下記の迷いが生じそう
- 最初からE2E書く?
- どこまで再利用されるかわかんないのにコンポーネント切り出す?
- 振る舞いがある程度自明なものもテスト書いてから?
その点、今回の例で使われているフォームはうってつけだ
ベテランと新人が一緒に改修に取り組むときに、ベテランが「下記のテストをクリアするように〇〇コンポーネントを変更してください」的なdraft PRをCI付きで出して、新人がそれを満たせるように実装する
みたいな教育的な用途で威力を発揮しそう
これすごいな
region ロールをアクセシブルネーム(タイトルそのもの)で絞り込み、含まれる link を検証しています。
const href = "https://testing.example.com/articles/2";
const title = "React18 の useId で a11y対応する";
const region = screen.getByRole("region", { name: title });
expect(within(region).getByRole("link")).toHaveAttribute("href", href);
前者・後者を見比べても、どういった行動をしているのか一目瞭然のテストとなり、a11y とテストが同時に改善できました。
コミットされたテストファイルを見れば、a11y を考慮しているコンポーネントなのか否か、これもまた一目瞭然です。コンポーネントのテストファイルを今一度確認し、a11y を考慮しているか見直してみましょう。
これすごいわ
a11yの改善はdocumentに意味を付与することでマシンリーダブル(あるいはヒューマンリーダブル via マシン)にすることなんだな。
そして、付与された意味を用いて記述することでデベロッパーリーダブルにもなるという。目からウロコだわ。
Takepepeさんは別途漁りたい感ある
これも別スクラップ建てよう、長いから
追記
Zennじゃないけどこれらには目を通しておきたいかも