5
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?

Bunのtestを触ってみる

Last updated at Posted at 2024-12-10

この記事はAteam LifeDesign Advent Calendar 2024の11日目記事です

はじめに

5周遅れぐらいですがアドカレの機会を使ってBunのtest周りを触ります

多いであろうNextJS+jestの構成からBunでのtestに移行しある程度Passするところまでやります

セットアップ

NextJS + jestの準備

まずは https://nextjs.org/docs/app/building-your-application/testing/jest を参考にNextJS + jestの環境を用意します

# PJ作成
npx create-next-app@latest --example with-jest with-jest-app

# 起動テスト
cd with-jest/
npm run dev

# テスト実行
npm test

# コンソールエラーが出て見づらいのでパッケージをアップデート
npm outdated
npm uninstall @testing-library/react
npm install -D @testing-library/react

# 記録用にテスト実行
npm test
テスト実行結果
 PASS  app/utils/add.test.ts
 PASS  app/page.test.tsx
 PASS  app/blog/[slug]/page.test.tsx
 PASS  __tests__/snapshot.tsx
 PASS  app/counter.test.tsx
 PASS  __tests__/index.test.tsx

Test Suites: 6 passed, 6 total
Tests:       6 passed, 6 total
Snapshots:   1 passed, 1 total
Time:        3.243 s
Ran all test suites.

bun testに移行する

https://bun.sh/ をinstallします

curl -fsSL https://bun.sh/install | bash
exec /bin/zsh 
bun --help

# テスト実行
bun test

この4行でテスト実行可能なところまでいけます
実行すると以下のエラー

bun testの結果
bun test v1.1.38 (bf2f153f)

app/page.test.tsx:
234 |     throw error;
235 |   }
236 |   if (!baseElement) {
237 |     // default to document.body instead of documentElement to avoid output of potentially-large
238 |     // head elements (such as JSS style blocks) in debug output
239 |     baseElement = document.body;
                        ^
ReferenceError: Can't find variable: document
      at render (/workspace/with-jest-app/node_modules/@testing-library/react/dist/pure.js:239:19)
      at /workspace/with-jest-app/app/page.test.tsx:8:3
✗ App Router: Works with Server Components [3.47ms]

app/counter.test.tsx:
234 |     throw error;
235 |   }
236 |   if (!baseElement) {
237 |     // default to document.body instead of documentElement to avoid output of potentially-large
238 |     // head elements (such as JSS style blocks) in debug output
239 |     baseElement = document.body;
                        ^
ReferenceError: Can't find variable: document
      at render (/workspace/with-jest-app/node_modules/@testing-library/react/dist/pure.js:239:19)
      at /workspace/with-jest-app/app/counter.test.tsx:8:3
✗ App Router: Works with Client Components (React State) [0.22ms]

__tests__/index.test.tsx:
234 |     throw error;
235 |   }
236 |   if (!baseElement) {
237 |     // default to document.body instead of documentElement to avoid output of potentially-large
238 |     // head elements (such as JSS style blocks) in debug output
239 |     baseElement = document.body;
                        ^
ReferenceError: Can't find variable: document
      at render (/workspace/with-jest-app/node_modules/@testing-library/react/dist/pure.js:239:19)
      at /workspace/with-jest-app/__tests__/index.test.tsx:9:5
✗ Home > renders a heading [0.75ms]

app/utils/add.test.ts:

# Unhandled error between tests
-------------------------------
1 | throw new Error(
          ^
error: This module cannot be imported from a Client Component module. It should only be used from a Server Component.
      at /workspace/with-jest-app/node_modules/server-only/index.js:1:7
-------------------------------


app/blog/[slug]/page.test.tsx:

# Unhandled error between tests
-------------------------------
 7 | import 'lodash/isEqualWith.js';
 8 | import 'lodash/uniq.js';
 9 | import 'css.escape';
10 | import 'aria-query';
11 | 
12 | expect.extend(extensions);
     ^
ReferenceError: Can't find variable: expect
      at /workspace/with-jest-app/node_modules/@testing-library/jest-dom/dist/index.mjs:12:1
-------------------------------


 0 pass
 5 fail
 2 errors
Ran 5 tests across 5 files. [284.00ms]

流石にそのままではダメそうですね

エラー内容と、bunがjestからの移行ガイドを出しているので見ながら直していきます

ReferenceError: Can't find variable: document

DOMにアクセスする場合はhappy-domの導入が必要みたいなので入れます

bun add -d @happy-dom/global-registrator
touch happydom.ts bunfig.toml
happydom.ts
import { GlobalRegistrator } from "@happy-dom/global-registrator";
GlobalRegistrator.register();
bunfig.toml
[test]
preload = "./happydom.ts"

toHaveTextContent is undefined

一部のマッチャーは使えないのでbunで使えるものに差し替えます

app/page.test.tsx
import { render, screen } from "@testing-library/react";
import Page from "./page";

it("App Router: Works with Server Components", () => {
  render(<Page />);
  // toHaveTextContentを変更
  expect(screen.getByRole("heading").textContent).toBe("App Router");
});

他のマッチャーも変えていきます

Found multiple elements with the role "heading"

他テストでrenderされたcomponentがそのまま残り続けているっぽい
もっと良い対応がありそうだが納期がやばいのでgetByTestIdに差し替えて対応する

app/counter.tsx
"use client";

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <>
      <h2 data-testid='count'>{count}</h2>
      <button type="button" onClick={() => setCount(count + 1)}>
        +
      </button>
    </>
  );
}
app/counter.test.tsx
import { fireEvent, render, screen } from "@testing-library/react";
import Counter from "./counter";

it("App Router: Works with Client Components (React State)", () => {
  render(<Counter />);
  expect(screen.getByTestId("count").textContent).toBe("0");
  fireEvent.click(screen.getByRole("button"));
  expect(screen.getByTestId("count").textContent).toBe("1");
});

TypeError: "/vercel.svg" cannot be parsed as a URL.

happy-dom側の問題のようです

対応策を納期内に見つけられなかったため一旦置きにしています

It should only be used from a Server Component

import "server-only";しているComponentのテストに失敗しています
NextJSの13.4.20からは成功するようになったよう

こちらもbun testで対応する方法が納期内に見つけられずでした

結果

最終的な実行結果
bun test v1.1.38 (bf2f153f)

app/page.test.tsx:
✓ App Router: Works with Server Components [20.93ms]

app/counter.test.tsx:
✓ App Router: Works with Client Components (React State) [12.84ms]

__tests__/index.test.tsx:

app/utils/add.test.ts:

app/blog/[slug]/page.test.tsx:
✓ App Router: Works with dynamic route segments [1.81ms]

 3 pass
 0 fail
 4 expect() calls
Ran 3 tests across 5 files. [360.00ms]

以上で3/5のテストの移行を行いました
残り2つについてはすぐには対応策を見つけられなかったので空き時間で調べます…

実行時間についてはテストケースが減ったとはいえ3.243 s → 360.00msと噂通り爆速でした
既にテストがあるプロダクトではエイヤで移行するのは厳しそうですが、
既存プロダクトに新規でテストを追加したり、新しいプロダクトへの採用であれば良さそうです

気になった点としては基本的な書き方は変わらないものの、現状ではまだ必要な変更点が多い点ですね
特にDOM周りの扱いやNextJSだとAppRouter周りに難がありそうでした
純粋なjs等のテストでは良さそうですがNextJSとかと組み合わせていくとまだまだ課題にぶち当たりそうでした

とはいえ早さは圧倒的なので個人的に小さいものを作ったりする時は積極的に採用していこうかと思います
以上で〆させていただきたいと思います
今回の教訓:締め切り駆動執筆はやめましょう

5
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
5
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?