STORES Product Blog

こだわりを持ったお商売を支える「STORES」のテクノロジー部門のメンバーによるブログです。

10行の変更でテストを85秒短縮

こんにちは。Webエンジニアをしているotariidaeです。

テストは速ければ速いほど良いものです。善は急げということでさっそく速くしていきましょう。

なにを速くするか

今回速くしていくのは STORES を利用いただいている事業者向け管理画面のバックエンドのテストです。2025年1月から本格的に開発され始めたアプリケーションなので、まだまだ最適化の余地がたっぷり残っていそうですね。Ruby on Rails製で、テストフレームワークはRSpecを使っています。

どうやって速くするか

RSpecを速くするためにまずやることといえば、そう、プロファイリングですね。

TestProfのサンプリング機能とStackProfを使ってコールスタックを取得して、speedscopeで可視化してみましょう。

speedscopeで可視化したコールスタックをSandwichビューのself time降順でソートした様子

見るからに怪しいKernel.sleepとKernel#sleepがあります。

勝手にsleepするのやめてね。こっちは仕事でRSpec回してるんですけど😠

今回はこの2つを倒していきます。

Kernel.sleepを速くする

フレームグラフを見ると、Kernel.sleepはどうやらretryable gem由来のようです。

Kernel.sleepのフレームグラフ

デフォルトでは1秒間sleepするようになっていたので、対策としてはこうですね。

# frozen_string_literal: true

Retryable.configure do |config|
  # テストではデフォルトのsleep時間を短くして高速化を図る
  config.sleep = 0.01
end

この6行で、19秒ほどかかっていたテスト群が4秒未満になりました。5倍速い!

Kernel#sleepを速くする

Kernel#sleepの方も探ってみると、自前でリトライ処理を実装している箇所でした。

Kernel#sleepのフレームグラフ

while attempts < @max_attempts && run.processing?
    sleep(@interval)
    run = @client.get_query_run(run.id)
    attempts += 1
end

該当コード

デフォルトのリトライ間隔はクラス定数で定義されていたので、対策としてはこうですね。

stub_const("Zuora::DataQuery::Runner::DEFAULT_INTERVAL", 0.01.second)

テストの構造に合わせて4箇所に設定して、1分20秒ほどかかっていたテスト群が10秒ほどになりました。8倍速い!

まとめ

TestProfとStackProfを使うことで楽に速くできるテストを見つけ、たった10行で85秒もテストの実行時間を短縮できました。

テスト最適化としてはかなり初歩的でしたが、もしかしたらみなさんの周りにも意外と探索されていない最適化の余地が残っているかもしれません。

STORES ではシュッとテストを速くしたいエンジニアを募集しています。

jobs.st.inc