126
116

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-12-05

ケース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内のテストコードの内容が一致すること/一致することがすぐわかること

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

126
116
3

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
126
116

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?