というオレオレルールなんですが、わりといい気がするのでご紹介します。
よくわからないと思うので実例で。
実例
狼が村人を噛むと村人は死ぬ。狼がハムスターを噛んでもハムスターは死なない という条件の rspec を、以下のように書きます。
require 'rails_helper'
RSpec.describe Wolf do
subject(:wolf){ FactoryGirl.build(:wolf) }
it { is_expected.to be_valid }
describe 'bite' do
subject { wolf.bite(creature) }
context 'bite villager' do
let(:creature){ FactoryGirl.build(:villager) }
it { expect{subject}.to change{creature.reload.living?}.to(false) }
end
context 'bite hamster' do
let(:creature){ FactoryGirl.build(:hamster) }
it { expect{subject}.not_to change{creature.reload.living?} }
end
end
end
順に上から見て行きましょう。
describe Wolf do
subject(:wolf){ FactoryGirl.build(:wolf) }
まず、この2行がペアです。 Wolf モデルに関するテストであるという宣言と、実際に wolf のインスタンスを用意する ruby 文とのペアです。 subject に引数を渡しているので、 wolf
でも subject
でも同じものを参照できます。
次の行。
it { is_expected.to be_valid }
is_expected
は expect(subject)
と等価です。実行文そのものがテスト内容を完全に表現しているので、 it
にコメントは不要でしょう。
その次の2行を見てみましょう。
describe 'bite' do
subject { wolf.bite(creature) }
biteメソッドをテストするよ!という宣言と、実際に wolf の bite メソッドを呼び出す ruby 文とのペアです。
先の subject で引数を渡しているので、 wolf
で参照できます。 creature
はこの時点では未定義です。
次の2行を御覧ください。
context 'bite villager' do
let(:creature){ FactoryGirl.build(:villager) }
villager を bite した時のテストだよ!という宣言と、biteに引数として渡してある creature
に villager のインスタンスを実際に渡す ruby 文とのペアです。
次の行はテストの本体になります。
it { expect{subject}.to change{creature.reload.living?}.to(false) }
今回は change マッチャの都合で is_expected
が使えませんが、英文として読んだ通りのテストなので、やはり it
にコメントは不要でしょう。
まとめ
describe と context の実装は alias なのでどっちを使っても実質同じなのですが、英単語の意味で使い分け、「何をテストするか」を describe, 「どんな状況のテストをするか」を context でまとめると分かりやすい、と言われています。
一方、subject と let も (ほぼ) alias でして、英単語の意味的に使い分けているだけなのですが、 subject は「何をテストするか」の宣言なので、これを describe と対応させ、 context は let で書くようにすると書き方に統一感が生まれ、読みやすい spec になるんでは、という提案でした。
完全にこのルールで全てを書けるわけではなく、describe と let がペアになるケースも実際に私の手元で発生はしていますが、 describeとかcontextのコメントを書いたら、次の行にそのコメントに対応するruby文を書く というルールは確実に可読性を上げるのでオススメです。