Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clean Test Code

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Clean Test Code

Rubyで読みやすいテストコードを書くためのコツについてまとめました

Avatar for Shinichi Maeshima

Shinichi Maeshima

May 18, 2017
Tweet

More Decks by Shinichi Maeshima

Other Decks in Technology

Transcript

  1. ࣗݾ঺հ • લౡਅҰ aka @willnet or @netwillnet • ginza.rb ͔Βདྷ·ͨ͠

    • ϑϦʔϥϯεRailsٕज़ސ໰ • https://github.com/willnet • https://twitter.com/netwillnet • http://blog.willnet.in
  2. # ྫ describe Stack do let!(:stack) { Stack.new } describe

    '#push' do context 'จࣈྻΛpushͨ͠ͱ͖' do before { stack.push('value') } it 'ฦΓ஋͕pushͨ͠஋Ͱ͋Δ͜ͱ' do expect(stack).to eq 'value' end end context 'nilΛpushͨ͠৔߹' do it 'ArgumentErrorʹͳΔ͜ͱ' do expect { stack.push(nil) }.to raise_error(ArgumentError) end end end describe '#pop' do # ུ end end
  3. # ҰݟDRYʹͰ͖ͦ͏ʹݟ͑Δ describe 'sample specs' do context 'a' do #

    ... end context 'b' do let!(:need_in_b_and_c) { ... } # ... end context 'c' do let!(:need_in_b_and_c) { ... } # ... end end
  4. describe User, type: :model do describe '#send_message' do let!(:sender) {

    create :user, name: 'maeshima' } let!(:receiver) { create :user, name: 'kamiya' } it 'ϝοηʔδ͕ਖ਼͘͠ૹΒΕΔ͜ͱ' do expect { sender.send_message(receiver: receiver, body: 'hello!') } .to change { Message.count }.by(1) end end end
  5. describe User, type: :model do describe '#send_message' do let!(:sender) {

    create :user } let!(:receiver) { create :user } it 'ϝοηʔδ͕ਖ਼͘͠ૹΒΕΔ͜ͱ' do expect { sender.send_message(receiver: receiver, body: 'hello!') } .to change { Message.count }.by(1) end end end
  6. ѱ͍ྫ FactoryGirl.define do factory :user do sequence(:name) { |i| "username#{i}"

    } after(:create) do |user, evaluator| create_list(:post, 2, user: user) end end end
  7. RSpec.describe User, type: :model do describe '#posts_ordered_by_popularity' do let!(:user) {

    create(:user) } let!(:post_popular) do post = user.posts[0] post.update(popularity: 5) post end let!(:post_not_popular) do post = user.posts[1] post.update(popularity: 1) post end it 'return posts ordered by populality' do expect(user.posts_ordered_by_popularity).to eq [post_popular, post_not_popular] end end end
  8. ྑ͍ྫ FactoryGirl.define do factory :user do sequence(:name) { |i| "username#{i}"

    } trait(:with_posts) do after(:create) do |user, evaluator| create_list(:post, 2, user: user) end end end end
  9. RSpec.describe User, type: :model do describe '#posts_ordered_by_popularity' do let!(:user) {

    create(:user) } let!(:post_popular) { create :post, user: user, popularity: 5 } let!(:post_not_popular) { create :post, user: user, popularity: 1 } it 'return posts ordered by populality' do expect(user.posts_ordered_by_popularity).to eq [post_popular, post_not_popular] end end end
  10. # ѱ͍ྫ class Post < ApplicationRecord # όάؚ͕·Ε͍ͯΔ scope :last_month_published,

    -> { where(publish_at: (Time.zone.now - 31.days).all_month) } end RSpec.describe Post, type: :model do describe '.last_month_published' do let!(: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' do Timecop.travel(2017, 5, 6) do # ςετ͸௨Δ expect(Post.last_month_published).to contain_exactly(april_1st, april_30th) end end end end
  11. # ྑ͍ྫ # ઈରͰ͸ͳ͍͕ෆ۩߹ʹؾͮ͘Մೳੑ͕૿͢ RSpec.describe Post, type: :model do describe

    '.last_month_published' do let!(: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' do expect(Post.last_month_published).to contain_exactly(last_beginning_of_month, last_end_of_month) end end end
  12. # ѱ͍ྫ describe Post do let!(: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 } end end end
  13. # ѱ͍ྫ describe Post do let!(: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' do let(:publish_at) { nil } it { is_expected.to eq false } end context 'when the post is closed' do let(:status) { :close } it { is_expected.to eq false } end context 'when the title includes "[WIP]"' do let(:title) { '[WIP]hello world'} it { is_expected.to eq false } end end end
  14. # ѱ͍ྫ # Ҿ਺ͱͯ͠౉͞Ε༵ͨ೔ʹରԠͨ͠෼͚ͩϙΠϯτΛ૿΍͢ϝιου # `Point#increase_by_day_of_the_week`Λςετ͍ͯ͠Δ RSpec.describe Point, type: :model

    do describe '#increase_by_day_of_the_week' do let(:point) { create :point, point: 0 } it_behaves_like 'point increasing by day of the week', 100 do let(:wday) { 0 } end it_behaves_like 'point increasing by day of the week', 50 do let(:wday) { 1 } end it_behaves_like 'point increasing by day of the week', 30 do let(:wday) { 2 } end # ... end end
  15. ఆٛ͸ͪ͜Β RSpec.shared_examples 'point increasing by day of the week' do

    |expected_point| it "increase by #{expected_point}" do expect(point.point).to eq 0 point.increase_by_day_of_the_week(wday) expect(point.point).to eq expected_point end end
  16. # ѱ͍ྫ(࠶ܝ) # - લఏ৚͕݅ଟ͍ # - ఆ͕ٛ෼ࢄ͍ͯ͠Δ RSpec.describe Point,

    type: :model do describe '#increase_by_day_of_the_week' do let(:point) { create :point, point: 0 } it_behaves_like 'point increasing by day of the week', 100 do let(:wday) { 0 } end it_behaves_like 'point increasing by day of the week', 50 do let(:wday) { 1 } end it_behaves_like 'point increasing by day of the week', 30 do let(:wday) { 2 } end # ... end end
  17. # shared_examples Λ࢖ͬͨ··ՄಡੑΛଟগ૿ͨ͠ྫ RSpec.describe Point, type: :model do describe '#increase_by_day_of_the_week'

    do let(:point) { create :point, point: 0 } context 'on sunday' do let(:wday) { 0 } it_behaves_like 'point increasing by day of the week', expected_point: 100 end context 'on monday' do let(:wday) { 1 } it_behaves_like 'point increasing by day of the week', expected_point: 50 end context 'on tuesday' do let(:wday) { 2 } it_behaves_like 'point increasing by day of the week', expected_point: 30 end # ... end end
  18. # ϒϩοΫҾ਺ΛύϥϝʔλҾ਺ʹͨ͠ RSpec.shared_examples 'point increasing by day of the week'

    do |expected_point:| it "increase by #{expected_point}" do expect(point.point).to eq 0 point.increase_by_day_of_the_week(wday) expect(point.point).to eq expected_point end end
  19. # shared_examples ΛࣙΊͨྫ RSpec.describe Point, type: :model do describe '#increase_by_day_of_the_week'

    do let(:point) { create :point, point: 0 } context 'on sunday' do let(:wday) { 0 } it "increase by 100" do expect(point.point).to eq 0 point.increase_by_day_of_the_week(wday) expect(point.point).to eq 100 end end context 'on monday' do let(:wday) { 1 } it "increase by 50" do expect(point.point).to eq 0 point.increase_by_day_of_the_week(wday) expect(point.point).to eq 50 end end # ུ end end