RuboCopカスタムルールでテストの記法統一と品質向上を実現した話

こんにちは、YOUTRUSTのしぶしゅん(YOUTRUST / X)です。 今回は、YOUTRUSTのNotificationJobに関するテストの品質向上を目的としたRuboCopカスタムルールの導入について紹介します。

なぜこの取り組みをしたかというと、NotificationJobのエンキューテストで引数を誤って記述していたため、ジョブはエンキューされるものの引数不備により実際の通知処理が実行されない状態のテストがありました。 この問題はYOUTRUSTのヘルパーメソッドを使っていれば防げていました。 そこで、RuboCopカスタムルールを導入し、テスト記法の統一と品質向上を図ることにしました。

NotificationJobについて

NotificationJobはYOUTRUSTの非同期通知処理を担当するジョブクラスです。 YOUTRUSTでは、ユーザーに対して様々な通知を行っており、100以上の通知タイプが存在します。 一例を上げると次のとおりです。

  • タイムラインフィードアイテム
    • フィードアイテムのいいね
    • フィードアイテムのコメント
  • チャットルームメッセージ
    • 新規メッセージ
    • グループルームに追加
  • コミュニティ
    • 新着投稿
    • 投稿へのコメント
  • 募集
    • 募集への応募

NotificationJobは通知の種類に合わせて必要な引数が異なります。 テストではNotificationJobがエンキューされることを確認するために、have_enqueued_job(NotificationJob)を使用していました。 しかしこれだけでは通知の種類に対応した引数の検証が不十分で、実際にはエンキューされていないケースがありました。

そこでNotificationJobのエンキューと引数の検証を一括で行うexpect_to_run_notification_jobというヘルパーメソッドを社内で作成しました。 このヘルパーメソッドは通知タイプごとに必要な引数を定義し、引数の内容まで含めた検証を行います。

ヘルパーメソッド

def expect_to_run_notification_job(type, &block)
  klass = NotificationJob::KLASS_BY_TYPE[type.to_sym]
  allow_to_receive_mocked_run_of_notification_job_target_class(klass)

  perform_enqueued_jobs { subject }

  args = block_given? ? block.call : nil
  if args
    expect(klass).to have_received(:run).with(args)
  else
    expect(klass).to have_received(:run)
  end
end

このヘルパーメソッドは以下の流れで動作します。

  1. 通知タイプから対応するクラスを取得
  2. そのクラスのrunメソッドをモック化
  3. テスト対象の処理を実行
  4. 引数が指定されている場合は引数込みで、されていない場合は実行のみを検証

ヘルパーメソッドへの置き換えが部分的に進んでいましたが、すべてのテストで使用されているわけではありませんでした。 今回のRuboCopカスタムルールを導入することで残った箇所を検知し、今後記述される場合もテスト記法を統一しました。

RuboCopカスタムルール Lint/EnforceNotificationJobMatcher の実装

have_enqueued_job(NotificationJob)expect_to_run_notification_job に置き換えるためのRuboCopカスタムルールを実装しました。

検知ロジック

  • 対象ファイル: spec/以下のテストファイル(spec/support/matchers.rbは除外)
  • 検知方法: AST(Abstract Syntax Tree)解析によるhave_enqueued_jobメソッド呼び出しのパターンマッチング
  • 引数チェック: have_enqueued_jobの引数がNotificationJobであることを確認

RuboCopルールの設定

# .rubocop.yml
require:
  - ./lib/rubocop/cop/lint/enforce_notification_job_matcher.rb

Lint/EnforceNotificationJobMatcher:
  Enabled: true

警告メッセージ

通知の種類により引数が異なるので自動で置換を行わず、手動での修正を促すための警告メッセージを設定しました。

  • "Use expect_to_run_notification_job instead of have_enqueued_job(NotificationJob) for consistency."

変更例

投稿に対するいいねのテストを例に、変更前と変更後のコードを示します。

# 変更前のエンキューのテスト
expect { subject }.to have_enqueued_job(NotificationJob)
  .with(
    'new_like_on_post',
    user_id: user_id,
    post_id: post_id,
    post_url: post_url
  )

# 変更後のヘルパーメソッドを使用したテスト
expect_to_run_notification_job('new_like_on_post') do
  {
    user_id: user_id,
    post_id: post_id,
    post_url: post_url
  }
end

導入後の効果

テストの統一

既存のテストコードを修正し、expect_to_run_notification_jobを使用することで、NotificationJobのテストが統一されました。 今後のテスト記述においても、NotificationJobのエンキューと引数の検証が一貫して行われるようになりました。

ミスの事前検知

RuboCopによる検知により、不適切なテスト記法を発見できるようになりました。 これまで見過ごしがちだった引数ミスを、開発者が自分で発見できるため、レビューの品質向上にもつながっています。

テスト記述速度の向上

統一されたヘルパーメソッドを使うことで、新しいNotificationJobのテストを書く際に迷いがなくなりました。 「どの記法を使うべきか」を考える時間が削減され、テスト記述に集中できるようになっています。

チーム全体の学習効果

カスタムルールの導入を通じて、チーム内でテスト品質に対する意識が向上しました。 「なぜこの記法が推奨されるのか」を議論することで、テスト設計への理解が深まり、他の領域でも同様の改善を検討するきっかけになっています。

最後に

昨年から比べてAIとより密に開発するようになりました。カスタムルールの記載もよいですが、今回のようなLint系のルールの整備も大事だと思います。 今後もチームでの開発体験を向上させるための取り組みを続けていきます。

YOUTRUSTではエンジニアを募集しています。 興味のある方はぜひご応募ください。

youtrust.jp

career.youtrust.co.jp