0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vitestでダウンロード機能のテストを書く

Last updated at Posted at 2025-02-02

はじめに

Vitest, React-Testing-Libraryでダウンロード機能のテストを書くことがありましたが、いろいろつまずいたので備忘録も兼ねて書きます。
フロントはVite, Reactで構築しています。

テスト対象

aタグを生成して、プログラム側でクリック、その後削除するコードです。
素でaタグを書いたらええやんと思われるかもですが、ダウンロードの前に何かしらの処理がしたい場合には、下のように書くと便利ですね。

const DownloadForm = () => {
    const handleDownload = () =>
        // ...何らかの処理
        const link = document.createElement("a")
        link.href = "downloadUrl"
        link.download = ""
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)
    }

    return (
        <button onClick={handleDownload}>ダウンロード</button>
    )
}

Error: Not implemented: navigation

まずはボタンの表示などの簡単なテストから書き始めていると、下のエラーが出ました。何やらテストライブラリ側でNavigation(URL遷移)をサポートしていないそうなのです。(JSDomあたり?)

Error: Not implemented: navigation (except hash changes)

documentをモックにして解決

navigationが変化したことをテストするのではなく、aタグの生成、クリック、削除の一連の動作が行われたことをテストします。

ハマったのが、documentのモックコードの位置です。it内の先頭やbefore内に書くと、documentが機能しなくなってしまうために、React周りやDOM操作を伴うテストなどが動かなくなります。なので諸々の処理が終わった後にモックを作成し、テストを書いています。
※より良い案があれば教えてくださいませ。

it("should file download", async () => {
    render(
        <MemoryRouter>
            <DownloadForm />
        </MemoryRouter>,
    )

    // ダウンロードボタンをクリック
    const downloadButton = screen.getByText("ダウンロード")
    fireEvent.click(downloadButton)

    // モックを作成
    const link = Object.assign(document.createElement("a"), {
        click: vi.fn(),
    })
    vi.spyOn(document, "createElement").mockImplementation(() => link)
    vi.spyOn(document.body, "appendChild").mockImplementation(vi.fn())
    vi.spyOn(document.body, "removeChild").mockImplementation(vi.fn())

    // ダウンロードのテスト
    await waitFor(() => {
        expect(document.body.appendChild).toHaveBeenCalledWith(link)
        expect(link.href).toBe(
        "downloadUrl",
        )
        expect(link.click).toHaveBeenCalledTimes(1)
        expect(document.body.removeChild).toHaveBeenCalledWith(link)
    })

    vi.restoreAllMocks() // モックを初期化
  })

おわりに

テストのモックというと、MSWを使ったり、ライブラリの関数をモックするなどしかやったことがありませんでした。このようにブラウザ側の機能もモックすることを覚えると色々なケースに対応できそうです。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?