⚠️

Jestだとerror instanceof Errorが正しく動かない不思議な経験をしたメモ

に公開

仕事中に、Node.jsでの実行時は問題ないのにjestのときだけ挙動が変わるという不思議な経験をしたので記事に残しておきます。

「何者だ」「エラーではありません」「よし通れ」

早速ですがサンプルコードです。
次のテストはjestでは通りません。 error instanceof Error がfalseになってしまうからです。

describe("存在しないURLにfetchでリクエストする", () => {
  it("発生する例外がErrorのインスタンスであること", async () => {
    try {
      // 存在しないURLを指定してTypeErrorを発生させる
      await fetch("http://notfound.localhost");
    } catch (error) {
      expect(typeof error).toBe("object"); // 通る
      expect(error.name).toBe("TypeError"); // 通る
      expect(error instanceof Error).toBe(true); // 通らない!!
    }
  });
});

もちろんNode.jsで実行するときには問題ありません。

$ node -e 'try { await fetch("http://notfound.localhost");
} catch (error) { console.log(error instanceof Error); }'
true

もう一つ例を出してみます。 process.envString[] 型ですが、次のテストもjestでは通りません。

describe("起動パラメータ", () => {
  it("起動パラメータが配列であること", () => {
    expect(process.argv instanceof Array).toBe(true); // 通らない!!
  });
});

こちらもNode.jsで実行するときには問題ありません。

$ node -e 'console.log(process.argv instanceof Array)'
true

ちなみにどちらのテストもvitestでは問題なくパスしました。

なぜこのようなことが起きるのか

このような初見殺しの挙動には多くの人が戸惑っており、jestのGitHubにもIssueが立っています。
https://github.com/jestjs/jest/issues/2549
このIssueが立ったのは2017年ですが、2025年6月現在もオープンのままです。

筆者も原因をちゃんと理解したわけではないのですが、この長編Issueを斜め読みしたところ、jestはテストごとにNode.jsのvmモジュールを使って新しい環境を作っているそうです。

Jest achieves test isolation between files by running each test in a separate VM context, giving each file a fresh global environment.
(意訳) Jestは各テストを別々のVM contextで実行することで各テストファイルごとの分離を実現し、各ファイルに新たなグローバル環境を提供します。
https://jestjs.io/blog/2025/06/04/jest-30#globals-cleanup-between-test-files

毎回新しい環境を作っているため、別の環境のものとinstanceofで比較するとtrueにならないってことなんですかね…。一番知りたいところがよくわかっていません。もし詳しい記事などあれば教えてもらえれば幸いです。

最初のサンプルがvitestでは問題ないのはJestとVitestのisolateについてという記事によると「vitestはvmによるisolateをしてない」そうなので、それが理由と思われます (vmの代わりにテストごとに新しいWorker Threadを使っている)。

回避策 (ワークアラウンド)

筆者が仕事で出くわしたのは error instanceof Error がjestのときだけfalseになってしまう問題だったのですが、このときは instanceof の代わりに上記Issueで紹介されているNode.jsの utilsモジュールのutils.types.isNativeError(value)を使って判定するように回避しました[1]

process.argv instanceof Array についても、Array.isArray(process.argv) のように判定すればjestであってもtrueを返してくれます。

この記事を書く際に実験したコードは以下にコミットしてあります。ブランチ vitest で同じテストをvitestで試すことも可能です。

https://github.com/mtgto/example-jest-instance-of

実行環境

  • macOS 15.5 (Apple Silicon)
  • Node.js v22.16.0
  • jest v30.0.0
  • vitest v3.2.3

参考資料

脚注
  1. Node v24からは utils.types.isNativeError は非推奨となりError.isError で判定しろとのこと。 ↩︎

Discussion