はじめに
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
クライアント自体をモック化することで解決することができました。
参考