Open9

Vitest × testing-library雑めも

koukou

全テストケース共通で利用する値などの設定方法

全てのテストファイルで共通で読み込んでおきたいような設定をどう定義するか。
(Laravelにおいて、TestCaseを拡張したBaseTestCaseクラスを定義して、そこのsetUp()メソッド内で共通で利用できる値等を定義するようなことがしたい)

setup用のファイルを用意して、Vitest(or Jest)の設定でそれを読み込むように設定をする

ルート直下等にvitest.setup.ts(jest.setup.ts)などのようなファイルを作成する。共通設定はここで読み込ませることになる。場所は別にどこでも構わない。
※testing-libraryは内部的にはVitest/Jestが動いているので、実際のテストランナーの方で設定をする必要がある。

【例】
全テストケースに対して、dayjsのlocale設定をJSTで適用させたいサンプル。

vitest.setup.ts
// NOTE: _app.tsxで読み込んでいるdayjsの設定と同じものを指定
import dayjs from 'dayjs';
import ja from 'dayjs/locale/ja';

dayjs.locale(ja);
vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'jsdom',
    // 上で作成したsetupファイルをここに指定する(Jestだと`setupFilesAfterEnv`らしい)
    setupFiles: ['./vitest.setup.ts'],
  },
  ...
});

これで、どのテストケースでもdayjsのlocale設定がJSTになった状態でテストすることができるようになる。
他にも、testing-libraryの拡張マッチャー(toBeInTheDocument()など)を使いたい際なども、このセットアップファイルを通して読み込ませることになる。

vitest.setup.ts
import '@testing-library/jest-dom' // 各テストケースで、toBeInTheDocumentなどが使えるようになる(※これでいけるのはJestの場合のみ。Vitestではこの後のスレッドの方法参照)
import dayjs from 'dayjs';
import ja from 'dayjs/locale/ja';

dayjs.locale(ja);

参考記事

koukou

Vitestでtesting-library/jest-domを使えるようにする

toBeInTheDocumentなどの便利な拡張マッチャーを使うために、@testing-library/jest-domパッケージを追加する必要があるが、その際テストランナーがVitestの場合にハマったのでメモ。

# インストール
yarn add -D @testing-library/jest-dom # 型も依存パッケージに含まれている

toBeInTheDocument等の拡張マッチャーは全てのテストファイルで共通で使える方が嬉しいため、前述のsetupファイルの中に以下を追記して読み込ませる。

Jestの場合

Jestの場合はsetupファイルにimport文を追記して終了。らくちん。

jest.setup.ts
import '@testing-library/jest-dom' // Jestであればこの追記のみで使えるようになる

// ... 他のセットアップ内容(テスト実行前に読み込またい処理)

Vitestの場合

Vitestの場合、Jestと同じような方法では以下のようなエラーが出てしまう。

vitest.setup.ts
import '@testing-library/jest-dom' // Jestと同様に設定したがエラーがでる

// ... 他のセットアップ内容(テスト実行前に読み込またい処理)

詳しい話は参考記事に譲るとして、以下のように自分でVitestのexpectを拡張することで、testing-library/jest-dom内の拡張マッチャーがVitestでも使えるようになる。

vitest.setup.ts
import matchers from '@testing-library/jest-dom/matchers' // 拡張マッチャーをimport
import { expect } from 'vitest';

expect.extend(matchers); // expectのextendメソッドに拡張マッチャーを指定して拡張

// ... 他のセットアップ内容(テスト実行前に読み込またい処理)

参考記事

追記

以下の記事では、普通にJest風のimportで設定が出来ているみたい?
なぜだろう。🤔
一応記事的に新しいのは今回の参考記事の方(と言っても2ヶ月程度の差だけれど)。

koukou

[未解決] VitestのAPIをグローバルで使えるように設定すると、toBeInTheDocumentなどが存在しないと言われる

Vitestの設定ファイルでdescribeexpectなどをグローバルで使えるようにする設定項目がある。

vitest.config.ts
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';

これでいい感じにグローバルでも使えるようになった(?)

koukou

@testing-library/user-eventの使い方が少し変わったらしい

Vitest無関係。@testing-library/user-eventの話。

v14から使用感が少し変わったとのこと。詳しくは参考記事。主に書き方の話。

# インストール
yarn add -D @testing-library/user-event # 型情報もこのパッケージ内に含まれている

参考記事

koukou

基本的には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();
});

参考記事

koukou

画像の表示をテストする方法

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属性の値が置き換えられてしまって上手く行かなかった?

参考記事

koukou

Jest/Vitestでもcontextを使えるようにする

前職時代に初めて触ったPHPのテストフレームワークで、Kahlan(カーラン)というものを使っていた際にテストコードを書く際は、「前提条件結果」の流れで書くと良いということを学んだ。

その際、Kahlanにはdescribecontext, it(=test)というキーワードがあり、

  • 前提 → describe(ex. 〇〇というデータがXX件存在する)
  • 条件 → context(ex. 〇〇をした場合)
  • 結果 → it(ex. 〇〇となること)

というふうに対応させて使っていたが、Jest/Vitestではcontextに対応したものが存在しない。

個人的には、contextが合ったほうがテストコードが見やすくて好きなので、個人プロジェクトにおいてはこれを導入したい。
その導入の仕方。

やり方的には、Jest/Vitestのsetupファイルに、describeのエイリアスとしてcontextをグローバルオブジェクトに定義するだけ。

vitest.setup.ts
global.context = describe

以下の解説記事が詳しい。

koukou

Vitestで実装コード内でインラインでテストを書く方法

こちらの記事((自分の) JavaScript のユニットテストの書き方)のコメントに有益コメントが書いてあった。
ただ、公式Docを見る限り、本番ビルド時に除く設定等もしておいた方が良さそうなので、ひとまず個人で使うようとして覚えておく。