GitHub Actionsで前回失敗したRSpecのテストケースを先に実行する
RSpecをGitHub Actionsで実行するWorkflowを作成しました。
同じPR内で前回失敗したテストケースがあれば、先にそれらを実行します。
これによりに失敗した箇所の修正がされたかをすばやく確認できます。
目的
前回テストに失敗した箇所が修正されたかを早く知るというのが目的です。
特に全体のテスト件数が多いとそれを知るまで時間がかることがあります。
修正が適切でない場合、同じ箇所のテストが再度失敗することがあるため、他のテストを実行するリソースや待ち時間の無駄を減らす狙いがあります。
実装
Rspecの設定
RSpecの実行結果を保存するファイルを設定します。
後でrspecの--only-failures
オプションを使って、前回失敗したテストのみ実行するため必要となります。
Gitで管理すると後述のWorkflowが意図した動きにならないためtmp
ディレクトリ配下にしています。
config.example_status_persistence_file_path = 'tmp/spec_examples.txt'
Workflowの作成
GitHub ActionsのWorkflowを作成します。以下がその例です
name: RSpec
on: pull_request
jobs:
rspec:
runs-on: ubuntu-latest
env:
RAILS_ENV: test
steps:
- uses: actions/checkout@v3
- name: Install
run: |
sudo apt-get update && sudo apt-get install -y curl build-essential git sqlite3 libsqlite3-dev
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.2.1
bundler-cache: true
- name: Restore test results
id: 'restore_test_results'
uses: actions/cache/restore@v3
with:
path: tmp/spec_examples.txt
key: ${{ runner.os }}-rspec-examples-${{ github.event.pull_request.number }}-${{ hashFiles('tmp/spec_examples.txt') }}
restore-keys: ${{ runner.os }}-rspec-examples-${{ github.event.pull_request.number }}-
- name: Run Rspec only failures
run: |
if [[ -e tmp/spec_examples.txt && $(grep -c 'failed' tmp/spec_examples.txt) -gt 0 ]]; then
grep -E "(example_id)|(---)|(failed)" tmp/spec_examples.txt
bundle exec rspec --format progress --only-failures
fi
- name: Run RSpec
run: |
bundle exec rspec
- name: Cache test results
if: always()
uses: actions/cache/save@v3
with:
path: tmp/spec_examples.txt
key: ${{ runner.os }}-rspec-examples-${{ github.event.pull_request.number }}-${{ hashFiles('tmp/spec_examples.txt') }}
実装は以上です。
Workflowの解説
テスト結果のキャッシュ
最後のステップでRspecの実行結果をキャッシュしています。
RSpecのテストが失敗してもキャッシュするためにif: always()
にしています。
また、PRごとにキャッシュするために${{ github.event.pull_request.number }}
をkey
に含めています。
- name: Cache test results
if: always()
uses: actions/cache/save@v3
with:
path: tmp/spec_examples.txt
key: ${{ runner.os }}-rspec-examples-${{ github.event.pull_request.number }}-${{ hashFiles('tmp/spec_examples.txt') }}
ちなみに、tmp/spec_examples.txt
の内容は以下のようになります。
example_id | status | run_time |
------------------------------------- | ------ | --------------- |
./spec/models/post_spec.rb[1:1:1:1:1] | passed | 0.22244 seconds |
./spec/models/post_spec.rb[1:1:1:2:1] | failed | 0.0217 seconds |
./spec/requests/posts_spec.rb[1:1:1] | passed | 0.22766 seconds |
前回失敗したテストケースの取得と実行
このステップで前回のテスト結果を取得しています。
key
は必須なので指定していますが、tmp/spec_examples.txt
はGitで管理していないため実質的に意味はありません。
実際にはrestore-keys
によりキャッシュが復元されます。
- name: Restore test results
id: 'restore_test_results'
uses: actions/cache/restore@v3
with:
path: tmp/spec_examples.txt
key: ${{ runner.os }}-rspec-examples-${{ github.event.pull_request.number }}-${{ hashFiles('tmp/spec_examples.txt') }}
restore-keys: ${{ runner.os }}-rspec-examples-${{ github.event.pull_request.number }}-
続くステップで、前回失敗したテストケースがある場合にのみrspecの--only-failures
オプションを指定してそれらを実行しています。
- name: Run Rspec only failures
run: |
if [[ -e tmp/spec_examples.txt && $(grep -c 'failed' tmp/spec_examples.txt) -gt 0 ]]; then
grep -E "(example_id)|(---)|(failed)" tmp/spec_examples.txt
bundle exec rspec --format progress --only-failures
fi
ちなみに、grep -c 'failed' tmp/spec_examples.txt
によるfailed
の存在確認はなくても動きます。すべてpassed
のとき無駄にファイルロードの時間がかかるのでこうしています。
解説は以上になります。
余談
最近、GitLabのHandbookを読んで作ってみようと思ったのがきっかけです。
以上
Discussion