0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Vibe Coding用】RSpec コーディングスタイルガイド作ってみた!

Posted at

最近流行りのVibe Codingをしていて細かく指示をしないと意図通りにならず、手直しが地味に手間でした。

特にRSpecの書き方は好みやチームで推奨されている書き方とエージェントが生成するものとの乖離が大きかったです。
こういうときに「RSpecコーディングガイドをプロンプトとして用意しておくと良いんだな〜」と思いつつ後回しになっていました。

「世界一流エンジニアの思考法」を再読している今思いたち、Geminiと一緒に「RSpec コーディングスタイルガイド」を作成しました。

使えそうな箇所は良しなにカスタマイズして使ってください!


RSpec コーディングスタイルガイド

このガイドラインは、チームにおけるRSpecのコーディングスタイルを統一し、可読性とメンテナンス性の高いテストコードを維持することを目的とします。


1. スタイリングとフォーマット

コードのレイアウト

letbefore などの定義ブロックと、it などの実行ブロックの間には空行を1つ入れ、論理的な区切りを明確にします。

context 'when user is active' do
  let(:active) { true }

  it { is_expected.to be true }
end

context のネスト

context がネストする場合、2階層目以降は and で始めることで、条件の組み合わせを分かりやすく表現します。

context 'when user is admin' do
  context 'and user is deleted' do
    # ...
  end
end

ハッシュ値の省略記法

ローカル変数と同じ名前のハッシュキーを渡す場合、Ruby 2.7以降の省略記法を積極的に使用します。

# good
let(:user) { create(:user, active:) }

2. ブロックの記述順序

describecontext 内では、以下の順序でブロックを記述します。

  1. subject
  2. let!
  3. let
  4. before
  5. it

3. データセットアップ

letbefore の使い分け

  • let / let!: it ブロック内で明示的に参照する変数を定義するために使用します。
  • before: it ブロック内では参照しませんが、テストの前提条件として必要なデータの作成に使用します。

context を活用した差分テスト

テストの前提条件に少しだけ差分がある場合は、親スコープでベースとなるオブジェクトを定義し、子の context差分となる値のみを let で上書きします。

describe '#active?' do
  subject { user.active? }

  let(:user) { create(:user, state:) }

  context 'when user is active' do
    let(:state) { :active }
    
    it { is_expected.to be true }
  end

  context 'when user is pending' do
    let(:state) { :pending }
    
    it { is_expected.to be false }
  end
end

let の上書きに関する注意

上記の「差分テスト」パターンは推奨されますが、let で定義された変数を、意図が分かりにくくなる形でまるごと上書きすることは避けます。テストの前提条件が大きく異なる場合は、context を分けるか、別の describe を作成することを検討してください。

before ブロックでのデータ作成

親子関係のあるデータを作成する場合、インスタンス変数 (@) を使わず、createブロック変数を用いて関連性を表現します。

before do
  create(:company) do |company|
    create(:user, company:)
  end
end

テストデータの更新方法

テストの前提条件としてデータを変更する場合、update メソッドで直接データを書き換えることは避けてください。状態が異なるデータが必要な場合は、create で別途作成します。

時刻の扱い (travel_to)

タイムスタンプが関わるテストでは、created_at などを直接上書きするのではなく、Active Supportの travel_to を使って時刻を固定します。

it 'creates a report for yesterday' do
  travel_to Time.current.yesterday do
    # ... 時刻が固定された状態でのテスト処理
  end
end

自動採番カラムの扱い

データベースによって自動で採番される id などのカラムは、テストデータ作成時に手動で値を指定しません。テスト内で値が必要な場合は、作成されたオブジェクトから参照します。

# BAD: idを直接指定している
let(:client) { create(:client, id: 123) }

# GOOD: 作成されたオブジェクトのidを参照する
let(:client) { create(:client) }

it do
  expect(subject).to eq({ id: client.id })
end

4. expect による検証

マッチャーの括弧

マッチャーに引数を渡す際の括弧 () は、原則として省略します。

expect(response).to have_http_status :ok

1 it あたりの expect

関連性の高い検証は have_attributesand を使って積極的にまとめます

it 'has correct attributes' do
  expect(user).to have_attributes(
    name: 'Taro',
    email: 'taro@example.com'
  )
end

change マッチャー

状況に応じて柔軟に使い分けます。レコード数の変化は .by を、属性値の変化は .from.to を使用します。

配列・ハッシュのマッチャー

部分一致の include よりも、完全一致の eq を優先します。順序を問わない完全一致には contain_exactly を使用します。

期待値の書き方

テストの期待値を検証する際は、**値を直接記述(ハードコーディング)**することを基本とします。これにより、テストコードを読むだけで期待する結果が明確にわかります。id のような動的な値のみ、変数から参照します。

it 'returns correct user data' do
  json = JSON.parse(response.body)
  expect(json).to eq(
    'id' => user.id,
    'name' => '山田太郎', # user.name とは書かない
    'rank' => 'gold'
  )
end

5. System Spec (Feature Spec) の書き方

Capybaraマッチャーの活用

System Specでは、find などで要素を取得してから検証するのではなく、Capybaraが提供する have_csshave_content といった専用のマッチャーを直接使います。これらのマッチャーには、非同期処理を待つ仕組みが組み込まれています。

# BAD: 要素を探してから検証している
node = find('.user_name')
expect(node).to have_content('山田太郎')

# GOOD: 専用マッチャーで一度に検証する
expect(page).to have_css('.user_name', text: '山田太郎')

# GOOD: `within`でスコープを絞り、可読性を上げる
within '.profile' do
  expect(page).to have_content('自己紹介文です')
end

6. モックとスタブ

  • 基本方針: 原則としてallowを使わずに同じデータを再現できるのが望ましいが、外部依存や他のオブジェクトとの連携は、スタブ (allow) を基本とします。
  • 定義場所: スタブの定義は before ブロック内で行います。

7. テストの共通化 (shared_examples)

  • 原則として使用しません。テストの可読性と具体性を優先します。例外的な使用はチームで合意の上、検討します。
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?