Help us understand the problem. What is going on with this article?

rspecの悪い書き方、良い書き方

More than 3 years have passed since last update.

ケース1

悪い書き方: itに文脈を書いてしまっている。

describe '#hoge' do
  it '引数がfooの場合barを返す' do
    expect(hoge('foo')).to eq 'bar'
  end
end

良い書き方: contextを使いましょう。itには期待値だけを書きましょう。

describe '#hoge' do
  context '引数がfooの場合' do
    let(:args) { 'foo' }
    it 'barを返す' do
      expect(hoge(args)).to eq 'bar'
    end
  end
end

ケース2

あまり良くない書き方: itsを使っていない

describe '#hoge' do
  it { expecte(hoge).to eq 'bar' }
end

良い書き方: itsを使いましょう。

its(:hoge) { is_expected.to eq 'bar' }

(itsを使うためにはrspec-its gemが必要です。)

ケース3

悪い書き方: contextの内容が直下のbeforeの内容と違う。

context 'ログインしている場合' do
  it '名前が表示される' do
    login(user)
    is_expected.to have_content 'Alice'
  end
end

良い書き方: beforeを使ってcontextの内容とbeforeの内容が同じになるようにしましょう。

context 'ログインしている場合' do
  before { login(user) }
  it '名前が表示される' do
    is_expected.to have_content 'Alice'
  end
end

ケース4

悪い書き方: letを使っていない。

describe User do
  before { @user = create(:user, name: 'Alice') }

  it '#name' do
    expect(@user.name).to eq 'Alice'
  end
end

良い書き方: letを使いましょう。

describe User do
  let(:user) { create(:user, name: 'Alice') }

  it '#name' do
    expect(user.name).to eq 'Alice'
  end
end

ケース5

悪い書き方: describesubjectが一致しない(subjectを使っていない)。

describe User do
  let(:user) { create(:user, name: 'Alice') }

  it '#name' do
    expect(user.name).to eq 'Alice'
  end
end

良い書き方: describeの内容とsubjectは一致させましょう。

describe User do
  let(:user) { create(:user, name: 'Alice') }
  subject { user }

  its(:name) { is_expected.to eq 'Alice' }
end

これでitsも使えるようになります。

ケース6

悪い書き方: factory_girlのリレーションを使っていない。

user_factory.rb
FactoryGirl.define do
  factory :user do
    company_id { rand(1..100) }
    name     { Faker::Name.name }
  end
end
let(:company) { create(:company) }
let(:user) { create(:user, company_id: company.id) }

良い書き方: リレーションを使いましょう。

user_factory.rb
FactoryGirl.define do
  factory :user do
    company
    name     { Faker::Name.name }
  end
end
let(:company) { create(:company) }
let(:user) { create(:user, company: company) }

ケース7

あまり良くない書き方: shouldaを使っていない。

context 'keyが空の場合' do
  before { user.key = '' }
  it { is_expected.to be_invalid }
end

良い書き方: shouldaを使いましょう。

it { is_expected.to validate_presence_of(:key) }

他に何ができるかは https://github.com/thoughtbot/shoulda-matchers を見てください。
かなり色々できます。

ケース8

悪い書き方: shouldを使っている。

it { foo.should eq 'bar' }

良い書き方: expectを使いましょう。

it { expect(foo).to eq 'bar' }

ケース9

あまり良くない書き方: 重いテストをまとめていない。

RSpec.feature 'Project List Page', js: true do
  before { visit project_list_path }
  subject { page }

  it { is_expected.to have_content 'ID' }
  it { is_expected.to have_content '名前' }
  it { is_expected.to have_content '受注先会社名' }
  it { is_expected.to have_content '開始日' }
  it { is_expected.to have_content '終了日' }
  it { is_expected.to have_content '契約日' }
end

良い書き方: 重いテストは1つにまとめましょう

「1つのテストで確認することは1つだけ」は正しいことですが、テストの実行にかかる時間とトレードオフです。
仮想ブラウザを使ったテストなどの重いテストはまとめましょう。

RSpec.feature 'Project List Page', js: true do
  before { visit project_list_path }
  subject { page }

  it '各項目が存在すること' do
    is_expected.to have_content 'ID' 
    is_expected.to have_content '名前' 
    is_expected.to have_content '受注先会社名' 
    is_expected.to have_content '開始日' 
    is_expected.to have_content '終了日' 
    is_expected.to have_content '契約日' 
  end
end

aggregate_failuresの使用

上記の例について、aggregate_failuresで各expectを囲ってやることでテストがfailした時にどの項目で落ちたのかがわかるようになります。

RSpec.feature 'Project List Page', js: true do
  before { visit project_list_path }
  subject { page }

  it '各項目が存在すること' do
    aggregate_failures 'testing items' do
      is_expected.to have_content 'ID' 
      is_expected.to have_content '名前' 
      is_expected.to have_content '受注先会社名' 
      is_expected.to have_content '開始日' 
      is_expected.to have_content '終了日' 
      is_expected.to have_content '契約日' 
    end
  end
end

spec_helperに以下の記述を追加することでも同じことが可能です。

config.define_derived_metadata do |meta|
  meta[:aggregate_failures] = true unless meta.key?(:aggregate_failures)
end

まとめ的な

specとは仕様という意味です。つまり、rspecは仕様書です。
その点をふまえて以下の点を気をつけるといいと思います。

  • context,describeを正しく使って仕様書として読みやすくすること
  • context,describeで謳っていることと、before,subject,it内のテストコードの内容が一致すること/一致することがすぐわかること

上記を守って楽しくプログラミング!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした