require'rails_helper'RSpec.describePost,type::modeldo describe '.last_month_published'dolet!(:april_1st) { create :post,publish_at:Time.zone.local(2017,4,1) }let!(:april_30th) { create :post,publish_at:Time.zone.local(2017,4,30) } before do create :post,publish_at:Time.zone.local(2017,5,1) create :post,publish_at:Time.zone.local(2017,3,31)end it 'return published posts in last month'doTimecop.travel(2017,5,6) doexpect(Post.last_month_published).tocontain_exactly(april_1st, april_30th)endendendend
このテストは常に成功するが、実装にはバグが含まれている。
テストを相対日時に変更してみる。
require'rails_helper'RSpec.describePost,type::modeldo describe '.last_month_published'dolet!(:now) { Time.zone.now }let!(:last_beginning_of_month) { create :post,publish_at:1.month.ago(now).beginning_of_month }let!(:last_end_of_month) { create :post,publish_at:1.month.ago(now).end_of_month } before do create :post,publish_at: now create :post,publish_at:2.months.ago(now)end it 'return published posts in last month'doexpect(Post.last_month_published).tocontain_exactly(last_beginning_of_month, last_end_of_month)endendend
let!(:user) { create :user,enabled: enabled }context 'when user is enabled'dolet(:enabled) { true } it { ... }endcontext 'when user is disabled'dolet(:enabled) { false } it { ... }end
RSpec.describePoint,type::modeldo describe '#increase_by_day_of_the_week'dolet(:point) { create :point,point:0 } it_behaves_like 'point increasing by day of the week',100dolet(:wday) { 0 }end it_behaves_like 'point increasing by day of the week',50dolet(:wday) { 1 }end it_behaves_like 'point increasing by day of the week',30dolet(:wday) { 2 }end# ...endend
どんな前提条件で結果として何を期待しているのか、これだけを見て理解できるだろうか。
定義は次のようになる。
RSpec.shared_examples'point increasing by day of the week'do|expected_point| it "increase by #{expected_point}"doexpect(point.point).to eq 0 point.increase_by_day_of_the_week(wday)expect(point.point).to eq expected_pointendend
RSpec.shared_examples'point increasing by day of the week'do|expected_point:| it "increase by #{expected_point}"doexpect(point.point).to eq 0 point.increase_by_day_of_the_week(wday)expect(point.point).to eq expected_pointendendRSpec.describePoint,type::modeldo describe '#increase_by_day_of_the_week'dolet(:point) { create :point,point:0 } context 'on sunday'dolet(:wday) { 0 } it_behaves_like 'point increasing by day of the week',expected_point:100end context 'on monday'dolet(:wday) { 1 } it_behaves_like 'point increasing by day of the week',expected_point:50end context 'on tuesday'dolet(:wday) { 2 } it_behaves_like 'point increasing by day of the week',expected_point:30end# ...endend
let!(:user) { create :user,enabled: enabled }context 'when user is enabled'dolet(:enabled) { true } it { ... }endcontext 'when user is disabled'dolet(:enabled) { false } it { ... }end
必要ないレコードを作らない
パフォーマンスの観点から、レコードを作らなくてすむ場合は作らないようにしたい。
describe 'posts#index'do context 'when visit /posts'dolet!(:posts) { create_list :post,100 } before { visit posts_path } it 'display all post titles'do posts.eachdo|post|expect(page).to have_content post.titleendendendend
RSpec.describePost,type::modeldolet!(:post) { create :post } describe '#published?'do subject { post.published? } context 'when the post has already published'do it { is_expected.to eq true }end context 'when the post has not published'do before { post.update(publish_at:nil) } it { is_expected.to eq false }end context 'when the post is closed'do before { post.update(status::close) } it { is_expected.to eq false }end context 'when the title includes "[WIP]"'do before { post.update(title:'[WIP]hello world') } it { is_expected.to eq false }endendend
RSpec.describePost,type::modeldolet!(:post) { create :post,title: title,status: status,publish_at: publish_at }let(:title) { 'hello world' }let(:status) { :open }let(:publish_at) { Time.zone.now } describe '#published?'do subject { post.published? } context 'when the post has already published'do it { is_expected.to eq true }end context 'when the post has not published'dolet(:publish_at) { nil } it { is_expected.to eq false }end context 'when the post is closed'dolet(:status) { :close } it { is_expected.to eq false }end context 'when the title includes "[WIP]"'dolet(:title) { '[WIP]hello world'} it { is_expected.to eq false }endendend