
CIでJestがハングした話と、LocalStackが有料化したのでFlociに移行した話
はじめに
「テストは全部パスしてるのに、CI が終わらない」
こんな経験はありませんか? 先日まさにこれに遭遇しました。GitHub Actions 上でサーバーのテスト(Jest)が 44件すべてパスした後、Jest プロセスが終了せずにジョブがハングし続けるという問題です。
調査を進める中で、ローカルで再現しようとしたところ、LocalStack が 2026年3月にコミュニティ版を廃止していたことも発覚。代替ツールを探して Floci にたどり着きました。
この記事では、その一連の調査と対応をまとめます。
CIでJestがハングする問題

症状
GitHub Actions のログを見ると、こんなメッセージが出ていました。
Tests: 44 passed, 44 total
Ran all test suites.
Jest did not exit one second after the test run has completed.
'This usually means that there are asynchronous operations that weren't stopped
in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot
this issue.'
テスト自体は全件パス。しかし Jest プロセスが終了せず、後続ステップが実行されないままジョブがタイムアウトかキャンセルを待ち続けます。
原因:オープンハンドル
Jest がプロセスを終了できない場合、多くはどこかで非同期の接続やタイマーが閉じられていないのが原因です。よくある例:
- AWS SDK クライアント(DynamoDB / S3 など)が接続を保持したまま
- HTTP サーバーが
close()されていない setInterval/setTimeoutがclearInterval/clearTimeoutされていない
調査:--detectOpenHandles
Jest には原因を特定するオプションがあります。
jest --detectOpenHandles
これをつけて実行すると、どのハンドルが残っているか、どのファイル・どの行で生成されたかをスタックトレース付きで出力してくれます。
● Jest has detected the following 1 open handle potentially keeping Jest from exiting:
● TCPWRAP
17 | const client = new DynamoDBClient({ region: "ap-northeast-1" });
| ^
このように、どの AWS SDK クライアントが原因かが一目でわかります。
暫定対応:--forceExit
根本原因の修正(afterAll でクライアントを destroy() する等)を進める前に、CI を止めないための暫定対応として --forceExit が使えます。
package.json のテストスクリプトに追加するだけです。
{
"scripts": {
"test": "jest --runInBand --forceExit"
}
}
--forceExit はテスト完了後に Jest を強制終了させます。オープンハンドルは残りますが、CI がハングしなくなります。--detectOpenHandles と併用すると、CI ログに診断情報を出しながら正常終了させることもできます。
"test": "jest --runInBand --detectOpenHandles --forceExit"
ポイント:ローカルで再現しない場合

今回、ローカルでは --detectOpenHandles をつけても問題が再現せず、Jest が正常終了しました。
理由は、ローカルでは AWS をエミュレートするモックツール(後述の Floci)が使われており、すべての AWS API コールがローカルに閉じているためです。CI では実際の AWS サービス(Kendra 等)への接続が残り、ハングしたと考えられます。
この場合、--forceExit で CI を正常終了させつつ、--detectOpenHandles のログで原因箇所を特定するアプローチが現実的です。
LocalStack が有料化していた
ローカルでの再現を試みようとしたとき、LocalStack を使おうとして壁にぶつかりました。
何が起きたか
License activation failed! 🔑❌
Reason: No credentials were found in the environment.
Please make sure to either set the LOCALSTACK_AUTH_TOKEN variable to a valid auth token.
以前は docker run localstack/localstack だけで起動できていたはずが、auth token を要求されて起動できません。
LocalStack コミュニティ版の廃止
調べると、LocalStack は 2026年3月23日にコミュニティ版(無料・ライセンス不要)を廃止していました。
| 時期 | 状況 |
|---|---|
| ~2026年3月 | localstack/localstack イメージが無料で使用可能 |
| 2026年3月以降 | すべてのイメージで auth token が必須に |
localstack/localstack:community-archive という過去バージョンのイメージも存在しますが、商用利用については別途確認が必要です。無料アカウントの free tier は「個人・非商用」向けと明示されています。
業務プロジェクトで使う場合は、商用ライセンスの取得か、代替ツールへの移行が必要です。
Floci:MIT ライセンスの LocalStack 互換ツール
LocalStack の代替を探したところ、Floci が有力候補でした。
Floci とは
- MIT ライセンス(商用利用可、無料)
- LocalStack Community のドロップイン代替(同じポート
4566、同じ認証情報形式) - 50以上の AWS サービスをエミュレート(DynamoDB, S3, SQS, Lambda 等)
- auth token 不要
LocalStack と同じエンドポイント(http://localhost:4566)を使うため、既存のコードを変更せずにそのまま切り替えられます。
セットアップ手順
1. Floci を起動する
docker run -d -p 4566:4566 floci/floci
起動確認:
curl -s http://localhost:4566/_localstack/health
"dynamodb":"running" と "s3":"running" が表示されれば OK です。
2. AWS リソースをセットアップする
Floci はデータを永続化しないため、起動のたびにテーブルやバケットを作成する必要があります。既存の LocalStack 用セットアップスクリプトがあれば、そのまま使えます。
ダミーの AWS 認証情報を設定するだけで動作します(Floci は認証情報を検証しません)。
AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test node scripts/setup.js
3. テストを実行する
AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test jest
Docker Desktop の代わりに Colima を使っている場合、先に Colima を起動します。
colima start
docker run -d -p 4566:4566 floci/floci
LocalStack との比較
| 項目 | LocalStack(2026年以降) | Floci |
|---|---|---|
| ライセンス | 商用利用には有料プラン必須 | MIT(商用利用可) |
| auth token | 必要 | 不要 |
| ポート | 4566 | 4566(互換) |
| 対応サービス数 | 多数(Pro プランで拡張) | 50以上 |
| 起動速度 | やや遅い | 速い(24ms) |
まとめ
今回の対応をまとめると:
- Jest のハング →
--detectOpenHandlesで原因特定、--forceExitで暫定対応。ローカルで再現しない場合は CI の実 AWS 接続が原因の可能性がある - LocalStack の有料化 → 2026年3月以降、業務利用では有料プランか代替ツールが必要
- Floci への移行 → MIT ライセンス、LocalStack 互換、auth token 不要でドロップイン置き換え可能
「ローカルで再現しないのにCIだけハングする」は厄介ですが、--detectOpenHandles --forceExit を CI に仕込んでログを見るのが地道ながら確実な調査手段です。LocalStack 有料化については、Floci のような代替ツールの選択肢が広がっているので、ライセンス要件に合ったツールを選ぶとよいと思います。


