51
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

お題は不問!Qiita Engineer Festa 2023で記事投稿!

テストCI実行時間を半分にした話

Last updated at Posted at 2023-07-05

はじめに

株式会社HRBrainでフロントエンジニアをやっているしっぽくんです。最近は担当プロダクトの脱monorepoだったりアーキテクチャ見直し、エラーログ解析などの改善作業を業務の傍らやっております。

今回はその中でCI時間の短縮を行ったよ、という話をします。

かなりターゲットは限定的なので、該当する人に届けばいいなと思います。

ターゲット(これらに合致する人向けです)

  • 利用技術スタック
    • TypeScript を使っている
    • テストプラットフォームは Jest
    • TypeScript のトランスパイルに ts-jest を使っている
  • CI時間が長くなり始めた

TL;DR

  • @swc/jest でトランスパイルするようにした
    • CIの実行時間が14分から6分の約半分ほどになった
  • jest.spyOn は使えないので、 jest.mock に書き直した
  • より早くしたい場合は vitestBun への移行を視野に入れよう

テストCIがいつまでも終わらないんだけど?!

弊社では GitHub Actions を使いCIを回しています。日々の開発のなかでプルリクを出しプロダクトにコミットを積み重ねていきますが、軽微な修正でもなかなかCIを終わらないことに気づきました。

遅いCI.jpg

え、CI完了までに14分もかかってる?!

テストケースもそれなりにありますが、とはいえ14分はかかりすぎです。豆を挽いてコーヒーを1杯入れる時間すらあります☕️

遅さの原因

端的に言えば ts-jest で型も含めたトランスパイルを行っていることが原因でした。

// jest.config.js
"transform": {
  "^.+\\.(ts|tsx)?$": "ts-jest"
},

設定ファイルに書いてたこれ

ではトランスパイルをしないようにすればいいというわけです。
とはいえメイン業務もあるので大きく変更するのは難しいです。(コスパよく対応したい...
そこで @swc/jest が候補に挙がりました。

To make your Jest tests run faster, you can swap out the default JavaScript-based runner (ts-jest) for a drop-in Rust replacement(opens in a new tab) using SWC.

設定もシンプルでほとんど差し替えする程度でした。(実際には設定ファイルを1つ追加しましたが、環境ごとに書く設定も違うので出しません)

module.exports = {
 transform: {
 "^.+\\.(t|j)sx?$": "@swc/jest",
 },
};

適当に設定を書き換えし実行してみました。
差し替えのみ.jpg

見事に半分以下に!

しかしそう簡単に移行はいかず、エラーがいくつか発生しました。(CI結果も落ちている😭)

立ちはだかる壁

ログを見ると複数のテストがこけており、中身はどれも jest.spyOn をつかっている場所でした。
エラー例: TypeError: Cannot redefine property: xxxx

調べてみると spyOn ではモジュールとしてインポートしたオブジェクトを書き換えています。 ECMAScript Modules(ESモジュール)ではモジュールの書き換えが基本的には不可能のため spyOn を利用している箇所で書き換えに失敗した結果になりました。

幸い spyOn を利用している箇所も少なかったため、 mock への置換を行うことで対処することにしました。

Jestは最新バージョンでESモジュールに対応しつつあるため、バージョンによっては今回の対応は不要になるかもしれません。

マイグレーション作業を下記で実施しました。

// spyOn実装
import HogeModule from 'hoge'
const spy = jest.spyOn(HogeModule, 'hogeMethod')

expect(spy.hogeMethod).toHaveBeenCalled()
// mock実装
jest.mock('hoge', () => {
  ...jest.requireActual('hoge'),
  hogeMethod: jest.fn()
})

import HogeModule from 'hoge'

expect(HogeModule.hogeMethod).toHaveBeenCalled()

改善結果

最終的には install ステップもキャッシュ対応し実行時間の改善することができました👏

改善結果.jpg

installもcache対応してテスト自体は4分に!

まとめ

遅いなと思ってから実装完了まで2時間半程度(内マイグレーション作業が2時間ほど)で改善できました。
設定ファイルだけ差し替えてCI回すだけならすぐ出来るので、まだ ts-jest 使っているよ!という方は検討してみてください。

ちなみに VitestBun への移行でさらに早くなるらしいのでより速さを求める人はこちらを。

参考文献

spyOnの実装

51
29
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
51
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?