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

Jest で open handle エラーが出た時の対処法

Last updated at Posted at 2025-04-27

はじめに

React アプリケーションで Supabase を使用したテストを実行すると、以下のようなエラーが発生しました。
このエラーの解決方法をまとめます。

Jest has detected the following 1 open handle potentially keeping Jest from exiting:

  ●  MESSAGEPORT

       9 | }
      10 |
    > 11 | export const supabase = createClient(supabaseUrl, supabaseKey);
         |                                     ^
      12 |

発端

テスト実行時にSupabaseへ接続していたため、テスト終了後も接続が切れず、Jestが「まだ開いている通信がある」と警告を出していました。

open handle(オープンハンドル)とは?
⇨まだ開いたままのリソース(通信・接続・タイマーなど)がある状態のこと

解決策

Supabaseクライアントのモック化

この問題を解決するには、テスト終了後にSupabaseとの接続を切る必要があります。
しかし、今回はテスト実行時にSupabase接続は行わず、Supabaseクライアントをモック化することで解決しました。

App.test.tsx
// Supabaseクライアントのモック化
jest.mock('@supabase/supabase-js', () => ({
  createClient: jest.fn(() => ({
    from: jest.fn(() => ({
      select: jest.fn().mockReturnThis(),
      eq: jest.fn().mockReturnThis(),
      single: jest.fn().mockResolvedValue({ data: null, error: null }),
      insert: jest.fn().mockResolvedValue({ data: null, error: null }),
      update: jest.fn().mockResolvedValue({ data: null, error: null }),
      delete: jest.fn().mockResolvedValue({ data: null, error: null }),
    })),
  })),
}));

// アプリケーション固有の関数のモック化
jest.mock('../lib/user', () => ({
  getUserById: jest.fn().mockResolvedValue({ user_id: 'testuser' }),
  // 他の関数...
}));

このモックは @supabase/supabase-js ライブラリの createClient 関数自体をモック化しています。
実際のSupabaseクライアントが初期化されないため、オープンハンドルは作成されません。


コード全体
App.test.tsx
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { App } from '../App';
import '@testing-library/jest-dom';
import { ChakraProvider } from '@chakra-ui/react';
import { getUserSkillById } from '../lib/userSkill';

/**
 * モックの作成
 */
// Supabaseクライアントのモック化
jest.mock('@supabase/supabase-js', () => ({
  createClient: jest.fn(() => ({
    from: jest.fn(() => ({
      select: jest.fn().mockReturnThis(),
      eq: jest.fn().mockReturnThis(),
      single: jest.fn().mockResolvedValue({ data: null, error: null }),
      insert: jest.fn().mockResolvedValue({ data: null, error: null }),
      update: jest.fn().mockResolvedValue({ data: null, error: null }),
      delete: jest.fn().mockResolvedValue({ data: null, error: null }),
    })),
  })),
}));

// アプリケーション固有の関数のモック化
jest.mock('../lib/userSkill', () => ({
  getUserSkillById: jest.fn(),
  getUserSkillForEdit: jest.fn(),
}));

jest.mock('../lib/user', () => ({
  insertUser: jest.fn().mockResolvedValue(true),
  updateUser: jest.fn().mockResolvedValue(true),
}));

// テストケース
describe('UserCard Component', () => {
  // テスト前後の処理
  beforeEach(() => jest.clearAllMocks());
  afterEach(() => jest.resetAllMocks());

  it('名刺カードの表示', async () => {
    // テストの実装
    // ...
  });
});

解決策のメリット

  • 根本的な解決 : createClient 自体をモック化するため、実際のSupabase接続が作られない
  • シンプル : 必要なメソッドだけモック化している
  • 安定性 : 外部依存がないので、テストが安定する

最後に

Supabaseを使ったReactアプリのテストで「オープンハンドル」問題が発生した場合は、Supabaseクライアント自体をモック化することで解決することができました。

参考

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