Vitest × testing-library雑めも
全テストケース共通で利用する値などの設定方法
全てのテストファイルで共通で読み込んでおきたいような設定をどう定義するか。
(Laravelにおいて、TestCaseを拡張したBaseTestCaseクラスを定義して、そこのsetUp()
メソッド内で共通で利用できる値等を定義するようなことがしたい)
setup用のファイルを用意して、Vitest(or Jest)の設定でそれを読み込むように設定をする
ルート直下等にvitest.setup.ts
(jest.setup.ts
)などのようなファイルを作成する。共通設定はここで読み込ませることになる。場所は別にどこでも構わない。
※testing-libraryは内部的にはVitest/Jestが動いているので、実際のテストランナーの方で設定をする必要がある。
【例】
全テストケースに対して、dayjsのlocale設定をJSTで適用させたいサンプル。
// NOTE: _app.tsxで読み込んでいるdayjsの設定と同じものを指定
import dayjs from 'dayjs';
import ja from 'dayjs/locale/ja';
dayjs.locale(ja);
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom',
// 上で作成したsetupファイルをここに指定する(Jestだと`setupFilesAfterEnv`らしい)
setupFiles: ['./vitest.setup.ts'],
},
...
});
これで、どのテストケースでもdayjsのlocale設定がJSTになった状態でテストすることができるようになる。
他にも、testing-libraryの拡張マッチャー(toBeInTheDocument()
など)を使いたい際なども、このセットアップファイルを通して読み込ませることになる。
import '@testing-library/jest-dom' // 各テストケースで、toBeInTheDocumentなどが使えるようになる(※これでいけるのはJestの場合のみ。Vitestではこの後のスレッドの方法参照)
import dayjs from 'dayjs';
import ja from 'dayjs/locale/ja';
dayjs.locale(ja);
参考記事
Vitestでtesting-library/jest-domを使えるようにする
toBeInTheDocument
などの便利な拡張マッチャーを使うために、@testing-library/jest-dom
パッケージを追加する必要があるが、その際テストランナーがVitestの場合にハマったのでメモ。
# インストール
yarn add -D @testing-library/jest-dom # 型も依存パッケージに含まれている
toBeInTheDocument
等の拡張マッチャーは全てのテストファイルで共通で使える方が嬉しいため、前述のsetupファイルの中に以下を追記して読み込ませる。
Jestの場合
Jestの場合はsetupファイルにimport文を追記して終了。らくちん。
import '@testing-library/jest-dom' // Jestであればこの追記のみで使えるようになる
// ... 他のセットアップ内容(テスト実行前に読み込またい処理)
Vitestの場合
Vitestの場合、Jestと同じような方法では以下のようなエラーが出てしまう。
import '@testing-library/jest-dom' // Jestと同様に設定したがエラーがでる
// ... 他のセットアップ内容(テスト実行前に読み込またい処理)
詳しい話は参考記事に譲るとして、以下のように自分でVitestのexpectを拡張することで、testing-library/jest-dom
内の拡張マッチャーがVitestでも使えるようになる。
import matchers from '@testing-library/jest-dom/matchers' // 拡張マッチャーをimport
import { expect } from 'vitest';
expect.extend(matchers); // expectのextendメソッドに拡張マッチャーを指定して拡張
// ... 他のセットアップ内容(テスト実行前に読み込またい処理)
参考記事
追記
以下の記事では、普通にJest風のimportで設定が出来ているみたい?
なぜだろう。🤔
一応記事的に新しいのは今回の参考記事の方(と言っても2ヶ月程度の差だけれど)。
[未解決] VitestのAPIをグローバルで使えるように設定すると、toBeInTheDocumentなどが存在しないと言われる
Vitestの設定ファイルでdescribe
やexpect
などをグローバルで使えるようにする設定項目がある。
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true // expectなどがimportなしで使えるようになる(see: https://vitest.dev/config/#globals)
environment: 'jsdom',
setupFiles: ['./vitest.setup.ts'],
},
...
});
ただ、この設定をしたらテストファイルの方で使っているexpect(hoge).toBeInTheDocument()
の.toBeInTheDocument()
の方で型エラーが出るようになった。
一旦globals
オプションをオフにして、都度expectなどをimportするように戻したが、なぜなんだろう。🤔
2023/08/15追記
tsconfig.jsonに以下追加
{
"compilerOptions": {
"types": ["vitest/globals"]
},
}
vitest.setup.tsに以下追加
import '@testing-library/jest-dom';
これでいい感じにグローバルでも使えるようになった(?)
@testing-library/user-eventの使い方が少し変わったらしい
Vitest無関係。@testing-library/user-event
の話。
v14から使用感が少し変わったとのこと。詳しくは参考記事。主に書き方の話。
# インストール
yarn add -D @testing-library/user-event # 型情報もこのパッケージ内に含まれている
参考記事
基本的にはgetBy〇〇
Vitest無関係。
レンダリングされた要素の取得系メソッドにはgetBy〇〇
やqueryBy〇〇
などあるが、基本的にはgetBy〇〇
を使い、レンダリング後に要素がちゃんと表示されていないことを確認したい場合にqueryBy〇〇
を使う。
違いは、getBy〇〇は対象の要素が見つからない場合に例外をスローするが、queryBy〇〇では例外をスローするのではなくnullを返す点が異なる。
トグルなどの表示切り替え後に、ちゃんと要素が消えているかを確認できる。
また、findBy〇〇
というのもあるが、これは対象要素がレンダリングされるのを少し待つことができる(1000ms = 1s)。要素が見つからないときはgetBy〇〇同様例外をスローする。
参考記事を見る感じ、ローディング → 表示みたいなUIのテストに使えるのかな?
実際の書き方
test('〇〇を表示できる', () => {
const { getByText, queryByText } = render(<Component />,);
// 要素が表示されていることをテストする(getByText + toBeInTheDocument)
expect(getByText('hogehoge')).toBeInTheDocument();
// 要素が表示されていないことをテストする(queryByText + toBeNull)
expect(queryByText('piyopiyo')).toBeNull();
});
参考記事
画像の表示をテストする方法
Vitest無関係。
getByAltText('文字列')
を用いることで、表示された要素の中から指定した文字列のaltタグを持つ要素を取得できる。
test('〇〇を表示できる', () => {
const { getByAltText } = render(<Component />,);
// 画像のalt属性が正しいかをテストする
expect(getByAltText('プロフィール画像')).toBeInTheDocument();
});
ただ、alt属性以外にsrc属性が正しいのかをテストしたいときなどもある。
そういうときは以下の方法で対応するみたい?
test('〇〇を表示できる', () => {
const { getByAltText } = render(<Component />,);
// そのままだとHTMLElementが返るので、キャストする(好きな方で)
const image = getByRole('img') as HTMLImageElement
const image = getByAltText('プロフィール画像') as HTMLImageElement
// 画像のsrc属性が正しいかをテストする(好きな方で)
expect(image.src).toBe('画像のURL');
expect(displayedImage.src).toContain('画像のURL');
expect(image).toHaveAttribute('src', '画像のURL')
});
ただ、Next.jsのNextImageを使う場合、src属性の値が置き換えられてしまって上手く行かなかった?
参考記事
今後読んでいきたい
- ReactでTesting Library/Jestを使ってテストを学ぼう
- これから始めるReact Testing - ①React Componentのテスト
- フロントエンドテストのまとめ
- React Testing Libraryの使い方
- React + Testing Library + Jestの覚書
- React テスト応用、テストに悩む人へ
- React Testing Libraryを使って、関数の中の非同期関数のテストをする。
- React Testing Libraryにチャレンジ 2 (現在進行形)
- Vue, Vitest, Testing Library, MSWを使ってテスト駆動開発するチュートリアル
- Testing with Next.js
- 実装例から見る React のテストの書き方
Jest/Vitestでもcontextを使えるようにする
前職時代に初めて触ったPHPのテストフレームワークで、Kahlan(カーラン)というものを使っていた際にテストコードを書く際は、「前提
→ 条件
→ 結果
」の流れで書くと良いということを学んだ。
その際、Kahlanにはdescribe
、context
, it(=test)
というキーワードがあり、
-
前提 →
describe
(ex. 〇〇というデータがXX件存在する) -
条件 →
context
(ex. 〇〇をした場合) -
結果 →
it
(ex. 〇〇となること)
というふうに対応させて使っていたが、Jest/Vitestではcontextに対応したものが存在しない。
個人的には、context
が合ったほうがテストコードが見やすくて好きなので、個人プロジェクトにおいてはこれを導入したい。
その導入の仕方。
やり方的には、Jest/Vitestのsetupファイルに、describe
のエイリアスとしてcontext
をグローバルオブジェクトに定義するだけ。
global.context = describe
以下の解説記事が詳しい。
Vitestで実装コード内でインラインでテストを書く方法
こちらの記事((自分の) JavaScript のユニットテストの書き方)のコメントに有益コメントが書いてあった。
ただ、公式Docを見る限り、本番ビルド時に除く設定等もしておいた方が良さそうなので、ひとまず個人で使うようとして覚えておく。