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

Readable RSpec

Readable RSpec

meguro.rb #4 @ Wantedly, Inc
読みやすいRSpecについての発表資料です。

Kento Nagata

June 21, 2017
Tweet

More Decks by Kento Nagata

Other Decks in Programming

Transcript

  1. Ӭా݈ਓ 8BOUFEMZ *OD8FC&OHJOFFS 8PSL 8BOUFEMZ *OD 8FC&OHJOFFS 8BOUFEMZ7JTJUͷ։ൃվળ 8BOUFEMZ5PPMTͷ։ൃվળ 

    7JJCBS *OD 8FC&OHJOFFS ϚονϯάαʔϏε͓Αͼ੍࡞ࢧԉπʔϧͷ։ൃ  !OHULOU !OHUL
  2. Table of contents * Describe and context * Use single-level

    contexts * Use let and before * Use subject and named subject * Reserved DRY * Expect change * Should not update context value * Set all significant fields on test data * Write expectations at a high level * Should not use "should" in our example names
  3. RSpec.describe Book do describe '#reserve' do it 'raises already reserved

    error when already reserved' do # ... end it 'reserves the book when the book is available' do # ... end end end Describe and context RSpec.describe Book do describe '#reserve' do context 'when already reserved' do it 'raises already reserved error' do # ... end end context 'the book is available' do it 'reserves the book' do # ... end end end end Better /PUGood
  4. RSpec.describe Book do describe '#reserve' do context 'when already reserved'

    do let(:reserved) { true } context 'when the users reservation limit is exceeded' do let(:exceeded) { true } # ... end context 'when the users reservation limit is not exceeded' do let(:exceeded) { false } # ... end end context 'the book is available' do let(:reserved) { false } context 'when the users reservation limit is exceeded' do let(:exceeded) { true } # ... end context 'when the users reservation limit is not exceeded' do let(:exceeded) { false } # ... end end end end Use single-level contexts RSpec.describe Book do describe '#reserve' do context 'when already reserved and the user reservation limit is exceeded' do let(:reserved) { true } let(:exceeded) { true } # ... end context 'when already reserved and the users reservation limit is not exceeded' do let(:reserved) { true } let(:exceeded) { false } # ... end context 'the book is available and the user reservation limit is exceeded' do let(:reserved) { false } let(:exceeded) { true } # ... end context 'the book is available and the user has enough reservation limit' do let(:reserved) { false } let(:exceeded) { false } # ... end end end Better /PUGood
  5. RSpec.describe Book do describe '#reserve' do context 'when already reserved'

    do let(:reserved) { true } context 'when the users reservation limit is exceeded' do let(:exceeded) { true } # ... end context 'when the users reservation limit is not exceeded' do let(:exceeded) { false } # ... end end context 'the book is available' do let(:reserved) { false } context 'when the users reservation limit is exceeded' do let(:exceeded) { true } # ... end context 'when the users reservation limit is not exceeded' do let(:exceeded) { false } # ... end end end end Use single-level contexts RSpec.describe Book do describe '#reserve' do context 'when already reserved and the user reservation limit is exceeded' do let(:reserved) { true } let(:exceeded) { true } # ... end context 'when already reserved and the users reservation limit is not exceeded' do let(:reserved) { true } let(:exceeded) { false } # ... end context 'the book is available and the user reservation limit is exceeded' do let(:reserved) { false } let(:exceeded) { true } # ... end context 'the book is available and the user has enough reservation limit' do let(:reserved) { false } let(:exceeded) { false } # ... end end end Better /PUGood RSpec.describe Book do describe '#reserve' do context 'when already reserved' do let(:reserved) { true } context 'when the users reservation limit is exceeded' do let(:exceeded) { true } # ... end end end /PUGood
  6. RSpec.describe Book do describe '#reserve' do context 'when already reserved'

    do let(:reserved) { true } context 'when the users reservation limit is exceeded' do let(:exceeded) { true } # ... end context 'when the users reservation limit is not exceeded' do let(:exceeded) { false } # ... end end context 'the book is available' do let(:reserved) { false } context 'when the users reservation limit is exceeded' do let(:exceeded) { true } # ... end context 'when the users reservation limit is not exceeded' do let(:exceeded) { false } # ... end end end end Use single-level contexts RSpec.describe Book do describe '#reserve' do context 'when already reserved and the user reservation limit is exceeded' do let(:reserved) { true } let(:exceeded) { true } # ... end context 'when already reserved and the users reservation limit is not exceeded' do let(:reserved) { true } let(:exceeded) { false } # ... end context 'the book is available and the user reservation limit is exceeded' do let(:reserved) { false } let(:exceeded) { true } # ... end context 'the book is available and the user has enough reservation limit' do let(:reserved) { false } let(:exceeded) { false } # ... end end end Better /PUGood RSpec.describe Book do describe '#reserve' do context 'when already reserved and the user reservation limit is exceeded' do let(:reserved) { true } let(:exceeded) { true } # ... end end end #FUUFS
  7. RSpec.describe Book do describe '#reserve' do context 'when already reserved'

    do let(:reserved) { true } context 'when the users reservation limit is exceeded' do let(:exceeded) { true } # ... end context 'when the users reservation limit is not exceeded' do let(:exceeded) { false } # ... end end context 'the book is available' do let(:reserved) { false } context 'when the users reservation limit is exceeded' do let(:exceeded) { true } # ... end context 'when the users reservation limit is not exceeded' do let(:exceeded) { false } # ... end end end end Use single-level contexts RSpec.describe Book do describe '#reserve' do context 'when already reserved and the user reservation limit is exceeded' do let(:reserved) { true } let(:exceeded) { true } # ... end context 'when already reserved and the users reservation limit is not exceeded' do let(:reserved) { true } let(:exceeded) { false } # ... end context 'the book is available and the user reservation limit is exceeded' do let(:reserved) { false } let(:exceeded) { true } # ... end context 'the book is available and the user has enough reservation limit' do let(:reserved) { false } let(:exceeded) { false } # ... end end end Better /PUGood RSpec.describe Book do describe '#reserve' do context 'when already reserved and the user reservation limit is exceeded' do let(:reserved) { true } let(:exceeded) { true } # ... end context 'when already reserved and the users reservation limit is not exceeded' do let(:reserved) { true } let(:exceeded) { false } # ... end end end #FUUFS
  8. Use let and before • letͱbeforeΛ࢖ͬͯcontextΛ໌֬ʹ͢Δ • context ϒϩοΫͷ௚Լʹॻ͘ •

    ੜ੒ޙʹࢀর͢ΔΦϒδΣΫτ͸letΛ࢖͏ • ੜ੒ޙʹࢀর͠ͳ͍ΦϒδΣΫτ͸beforeΛ࢖͏ • Πϯελϯεม਺͸࢖Θͳ͍ • typoͯ͠΋ؾ෇͖ͮΒ͍ • `before(:each)`͸ͲͷexampleͰ΋૸ΔͨΊແବʹΞαΠϯ͞ΕΔ • ϩʔΧϧม਺ͱࠞࡏ͢ΔͱϦϑΝΫλͮ͠Β͍
  9. # Case 1 RSpec.describe Book do describe '#reserve' do context

    'when the book is already reserved' do it 'returns falsey value' do reserved = true book = Book.new(reserved: reserved) book.reserve expect(book.reserved).to be_falsey end end end end # Case 2 RSpec.describe Book do describe '#reserve' do context 'when the book is already reserved' do before do @reserved = true @book = Book.new(reserved: reserved) @book.reserve end it 'returns falsey value' do expect(@book.reserve).to be_falsey end end end end Use let and before RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(reserved: reserved) } context 'when the book is already reserved' do let(:reserved) { true } it 'returns falsey value' do is_expected.to be_falsey end end end end Better /PUGood
  10. # Case 1 RSpec.describe Book do describe '#reserve' do context

    'when the book is already reserved' do it 'returns falsey value' do reserved = true book = Book.new(reserved: reserved) book.reserve expect(book.reserved).to be_falsey end end end end # Case 2 RSpec.describe Book do describe '#reserve' do context 'when the book is already reserved' do before do @reserved = true @book = Book.new(reserved: reserved) @book.reserve end it 'returns falsey value' do expect(@book.reserve).to be_falsey end end end end Use let and before RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(reserved: reserved) } context 'when the book is already reserved' do let(:reserved) { true } it 'returns falsey value' do is_expected.to be_falsey end end end end Better /PUGood # Case 1 RSpec.describe Book do describe '#reserve' do context 'when the book is already reserved' do it 'returns falsey value' do reserved = true book = Book.new(reserved: reserved) book.reserve expect(book.reserved).to be_falsey end end end end /PUGood DPOUFYU͕Կ͔͕Θ͔ΓͮΒ͍
  11. # Case 1 RSpec.describe Book do describe '#reserve' do context

    'when the book is already reserved' do it 'returns falsey value' do reserved = true book = Book.new(reserved: reserved) book.reserve expect(book.reserved).to be_falsey end end end end # Case 2 RSpec.describe Book do describe '#reserve' do context 'when the book is already reserved' do before do @reserved = true @book = Book.new(reserved: reserved) @book.reserve end it 'returns falsey value' do expect(@book.reserve).to be_falsey end end end end Use let and before RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(reserved: reserved) } context 'when the book is already reserved' do let(:reserved) { true } it 'returns falsey value' do is_expected.to be_falsey end end end end Better /PUGood /PUGood DPOUFYU͕Կ͔͕Θ͔ΓͮΒ͍ # Case 1 RSpec.describe Book do describe '#reserve' do context 'when the book is already reserved' do it 'returns falsey value' do reserved = true book = Book.new(reserved: reserved) book.reserve expect(book.reserved).to be_falsey end end end end ͕͜͜DPOUFYU
  12. # Case 1 RSpec.describe Book do describe '#reserve' do context

    'when the book is already reserved' do it 'returns falsey value' do reserved = true book = Book.new(reserved: reserved) book.reserve expect(book.reserved).to be_falsey end end end end # Case 2 RSpec.describe Book do describe '#reserve' do context 'when the book is already reserved' do before do @reserved = true @book = Book.new(reserved: reserved) @book.reserve end it 'returns falsey value' do expect(@book.reserve).to be_falsey end end end end Use let and before RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(reserved: reserved) } context 'when the book is already reserved' do let(:reserved) { true } it 'returns falsey value' do is_expected.to be_falsey end end end end Better /PUGood # Case 2 RSpec.describe Book do describe '#reserve' do context 'when the book is already reserved' do before do @reserved = true @book = Book.new(reserved: reserved) @book.reserve end it 'returns falsey value' do expect(@book.reserve).to be_falsey end end end end /PUGood *OTUBODFWBSJBCMFΛ࢖Θͳ͍
  13. # Case 1 RSpec.describe Book do describe '#reserve' do context

    'when the book is already reserved' do it 'returns falsey value' do reserved = true book = Book.new(reserved: reserved) book.reserve expect(book.reserved).to be_falsey end end end end # Case 2 RSpec.describe Book do describe '#reserve' do context 'when the book is already reserved' do before do @reserved = true @book = Book.new(reserved: reserved) @book.reserve end it 'returns falsey value' do expect(@book.reserve).to be_falsey end end end end Use let and before RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(reserved: reserved) } context 'when the book is already reserved' do let(:reserved) { true } it 'returns falsey value' do is_expected.to be_falsey end end end end Better /PUGood /PUGood Πϯελϯεม਺Λ࢖Θͳ͍ # Case 2 RSpec.describe Book do describe '#reserve' do context 'when the book is already reserved' do before do @reserved = true @book = Book.new(reserved: reserved) @book.reserve end it 'returns falsey value' do expect(@book.reserve).to be_falsey end end end end FYBNQMFຖͰݺ͹Εͯ͠·͏ ϩʔΧϧม਺ͱͷ۠ผ͕ ೉͘͠ͳΔ
  14. RSpec.describe Book do describe '#reserve' do context 'when the book

    is already reserved' do it 'returns falsey value' do reserved = true book = Book.new(reserved: reserved) book.reserve expect(book.reserved).to be_truthy end end end end RSpec.describe Book do describe '#reserve' do before do @reserved = true @book = Book.new(reserved: reserved) @book.reserve end it 'returns falsey value' do expect(@book.reserve).to be_falsey end end end Use let and before RSpec.describe Book do describe '#reserve' do context 'when already reserved and the user reservation limit is exceeded' do let(:reserved) { true } let(:exceeded) { true } # ... end context 'when already reserved and the users reservation limit is not exceeded' do let(:reserved) { true } let(:exceeded) { false } # ... end context 'the book is available and the user reservation limit is exceeded' do let(:reserved) { false } let(:exceeded) { true } # ... end context 'the book is available and the user has enough reservation limit' do let(:reserved) { false } let(:exceeded) { false } # ... end end end Better /PUGood RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(reserved: reserved) } context 'when the book is already reserved' do let(:reserved) { true } it 'returns falsey value' do is_expected.to be_falsey end end end end #FUUFS
  15. RSpec.describe Book do describe '#reserve' do context 'when the book

    is already reserved' do it 'returns falsey value' do reserved = true book = Book.new(reserved: reserved) book.reserve expect(book.reserved).to be_truthy end end end end RSpec.describe Book do describe '#reserve' do before do @reserved = true @book = Book.new(reserved: reserved) @book.reserve end it 'returns falsey value' do expect(@book.reserve).to be_falsey end end end Use let and before RSpec.describe Book do describe '#reserve' do context 'when already reserved and the user reservation limit is exceeded' do let(:reserved) { true } let(:exceeded) { true } # ... end context 'when already reserved and the users reservation limit is not exceeded' do let(:reserved) { true } let(:exceeded) { false } # ... end context 'the book is available and the user reservation limit is exceeded' do let(:reserved) { false } let(:exceeded) { true } # ... end context 'the book is available and the user has enough reservation limit' do let(:reserved) { false } let(:exceeded) { false } # ... end end end Better /PUGood #FUUFS RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(reserved: reserved) } context 'when the book is already reserved' do let(:reserved) { true } it 'returns falsey value' do is_expected.to be_falsey end end end end DPOUFYUͱMFU͕ରԠ͍ͯͯ͠
 ಡΈ΍͍͢
  16. RSpec.describe Book do describe '#reserve' do let(:book) { Book.new(reserved: false)

    } let(:user) { User.new } it 'returns truthy value' do expect(book.reserve(user)).to be_truthy end it 'reserved user returns correct user' do book.reserve(user) expect(book.reserved_user).to eq user end end end Use subject and named subject RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new(reserved: false) } let(:user) { User.new } it 'returns truthy value' do is_expected.to be_truthy end it 'reserved user returns correct user' do reserve_book expect(book.reserved_user).to eq user end it 'number of books that the user reserved increases by 1' do expect { reserve_book }.to change { user.reserved_books.count }.by(1) end end end Better /PUGood
  17. RSpec.describe Book do describe '#reserve' do let(:book) { Book.new(reserved: false)

    } let(:user) { User.new } it 'returns truthy value' do expect(book.reserve(user)).to be_truthy end it 'reserved user returns correct user' do book.reserve(user) expect(book.reserved_user).to eq user end end end Use subject and named subject RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new(reserved: false) } let(:user) { User.new } it 'returns truthy value' do is_expected.to be_truthy end it 'reserved user returns correct user' do reserve_book expect(book.reserved_user).to eq user end it 'number of books that the user reserved increases by 1' do expect { reserve_book }.to change { user.reserved_books.count }.by(1) end end end Better /PUGood /PUGood ςετओମ͕Θ͔ΓͮΒ͍ RSpec.describe Book do describe '#reserve' do let(:book) { Book.new(reserved: false) } let(:user) { User.new } it 'returns truthy value' do expect(book.reserve(user)).to be_truthy end it 'reserved user returns correct user' do book.reserve(user) expect(book.reserved_user).to eq user end end end
  18. RSpec.describe Book do describe '#reserve' do let(:book) { Book.new(reserved: false)

    } let(:user) { User.new } it 'returns truthy value' do expect(book.reserve(user)).to be_truthy end it 'reserved user returns correct user' do book.reserve(user) expect(book.reserved_user).to eq user end end end Use subject and named subject RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new(reserved: false) } let(:user) { User.new } it 'returns truthy value' do is_expected.to be_truthy end it 'reserved user returns correct user' do reserve_book expect(book.reserved_user).to eq user end it 'number of books that the user reserved increases by 1' do expect { reserve_book }.to change { user.reserved_books.count }.by(1) end end end Better /PUGood #FUUFS RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(reserved: false) } let(:user) { User.new } it 'returns truthy value' do is_expected.to be_truthy end it 'reserved user returns correct user' do subject expect(book.reserved_user).to eq user end end end ςετओମ͕໌͔֬ͭ%3:
  19. RSpec.describe Book do describe '#reserve' do let(:book) { Book.new(reserved: false)

    } let(:user) { User.new } it 'returns truthy value' do expect(book.reserve(user)).to be_truthy end it 'reserved user returns correct user' do book.reserve(user) expect(book.reserved_user).to eq user end end end Use subject and named subject RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new(reserved: false) } let(:user) { User.new } it 'returns truthy value' do is_expected.to be_truthy end it 'reserved user returns correct user' do reserve_book expect(book.reserved_user).to eq user end it 'number of books that the user reserved increases by 1' do expect { reserve_book }.to change { user.reserved_books.count }.by(1) end end end Better /PUGood #FUUFS✨ RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new(reserved: false) } let(:user) { User.new } it 'reserved user returns correct user' do reserve_book expect(book.reserved_user).to eq user end end end ओޠ͕ԿΛҙຯ͢Δͷ͔͕໌֬ʹͳΔ
  20. RSpec.describe Book do describe '#reserve' do let(:book) { Book.new(reserved: false)

    } let(:user) { User.new } it 'returns truthy value' do expect(book.reserve(user)).to be_truthy end it 'reserved user returns correct user' do book.reserve(user) expect(book.reserved_user).to eq user end end end Use subject and named subject RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new(reserved: false) } let(:user) { User.new } it 'returns truthy value' do is_expected.to be_truthy end it 'reserved user returns correct user' do reserve_book expect(book.reserved_user).to eq user end it 'number of books that the user reserved increases by 1' do expect { reserve_book }.to change { user.reserved_books.count }.by(1) end end end Better /PUGood #FUUFS✨ DIBOHFFYQFDUBUJPO͸ಛʹݟ΍͍͢ RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new(reserved: false) } let(:user) { User.new } it 'number of books that the user reserved increases by 1' do expect { reserve_book }.to change { user.reserved_books.count }.by(1) end end end
  21. Reserved DRY • DRY(Don’t Repeat Yourself)͸߇͑Ίʹ͢Δ • ৑௕ੑʹΑΔมߋͷͮ͠Β͞ΑΓɺ
 ಡΈʹ͘͞ͷํ͕໰୊ʹͳΔέʔε͕ଟ͍ •

    shared example΍macroΛ࢖͏৔߹
 ͦͷந৅ԽʹΑͬͯಘΒΕͨ΋ͷͱՄಡੑͷόϥϯεΛߟ͑Δ΂͖
  22. RSpec.shared_examples 'reservation' do |decrease_by:, has_due_date:| it "reserve the book and

    decrease remaining by -#{decrease_by}" do expect { reserve_book }.to change { user.remaining_count }.by(-decrease_by) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be has_due_date end end RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new } context 'when user has admin role' do let(:user) { User.new(role: [:admin]) } it_bahaves_like "reservation", decrease_by: 0, has_due_date: false end context 'when user has normal role' do let(:user) { User.new(role: [:normal]) } it_bahaves_like "reservation", decrease_by: 1, has_due_date: true end end end Reserved DRY RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new } context 'when user has admin role' do let(:user) { User.new(role: [:admin]) } it "reserve and decrease remaining count and has no due date" do expect { reserve_book }.not_to change { user.remaining_count } expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be_falsey end end context 'when user has normal role' do let(:user) { User.new(role: [:normal]) } it "reserve and decrease remaining count and has no due date" do expect { reserve_book }.to change { user.remaining_count }.by(1) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be_truthy end end end end Better /PUGood
  23. RSpec.shared_examples 'reservation' do |decrease_by:, has_due_date:| it "reserve the book and

    decrease remaining by -#{decrease_by}" do expect { reserve_book }.to change { user.remaining_count }.by(-decrease_by) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be has_due_date end end RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new } context 'when user has admin role' do let(:user) { User.new(role: [:admin]) } it_bahaves_like "reservation", decrease_by: 0, has_due_date: false end context 'when user has normal role' do let(:user) { User.new(role: [:normal]) } it_bahaves_like "reservation", decrease_by: 1, has_due_date: true end end end Reserved DRY RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new } context 'when user has admin role' do let(:user) { User.new(role: [:admin]) } it "reserve and decrease remaining count and has no due date" do expect { reserve_book }.not_to change { user.remaining_count } expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be_falsey end end context 'when user has normal role' do let(:user) { User.new(role: [:normal]) } it "reserve and decrease remaining count and has no due date" do expect { reserve_book }.to change { user.remaining_count }.by(1) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be_truthy end end end end Better /PUGood /PUGood ඞཁͳҾ਺ɺ৚͕݅Θ͔ΓͮΒ͍ RSpec.shared_examples 'reservation' do |decrease_by:, has_due_date:| it "reserve the book and decrease remaining by -#{decrease_by}" do expect { reserve_book }.to change { user.remaining_count }.by(-decrease_by) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be has_due_date end end
  24. RSpec.shared_examples 'reservation' do |decrease_by:, has_due_date:| it "reserve the book and

    decrease remaining by -#{decrease_by}" do expect { reserve_book }.to change { user.remaining_count }.by(-decrease_by) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be has_due_date end end RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new } context 'when user has admin role' do let(:user) { User.new(role: [:admin]) } it_bahaves_like "reservation", decrease_by: 0, has_due_date: false end context 'when user has normal role' do let(:user) { User.new(role: [:normal]) } it_bahaves_like "reservation", decrease_by: 1, has_due_date: true end end end Reserved DRY RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new } context 'when user has admin role' do let(:user) { User.new(role: [:admin]) } it "reserve and decrease remaining count and has no due date" do expect { reserve_book }.not_to change { user.remaining_count } expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be_falsey end end context 'when user has normal role' do let(:user) { User.new(role: [:normal]) } it "reserve and decrease remaining count and has no due date" do expect { reserve_book }.to change { user.remaining_count }.by(1) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be_truthy end end end end Better /PUGood /PUGood ඞཁͳҾ਺ɺ৚͕݅Θ͔ΓͮΒ͍ RSpec.shared_examples 'reservation' do |decrease_by:, has_due_date:| it "reserve the book and decrease remaining by -#{decrease_by}" do expect { reserve_book }.to change { user.remaining_count }.by(-decrease_by) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be has_due_date end end
  25. RSpec.shared_examples 'reservation' do |decrease_by:, has_due_date:| it "reserve the book and

    decrease remaining by -#{decrease_by}" do expect { reserve_book }.to change { user.remaining_count }.by(-decrease_by) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be has_due_date end end RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new } context 'when user has admin role' do let(:user) { User.new(role: [:admin]) } it_bahaves_like "reservation", decrease_by: 0, has_due_date: false end context 'when user has normal role' do let(:user) { User.new(role: [:normal]) } it_bahaves_like "reservation", decrease_by: 1, has_due_date: true end end end Reserved DRY RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new } context 'when user has admin role' do let(:user) { User.new(role: [:admin]) } it "reserve and decrease remaining count and has no due date" do expect { reserve_book }.not_to change { user.remaining_count } expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be_falsey end end context 'when user has normal role' do let(:user) { User.new(role: [:normal]) } it "reserve and decrease remaining count and has no due date" do expect { reserve_book }.to change { user.remaining_count }.by(1) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be_truthy end end end end Better /PUGood /PUGood ඞཁͳҾ਺ɺ৚͕݅Θ͔ΓͮΒ͍ RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new } context 'when user has admin role' do let(:user) { User.new(role: [:admin]) } it_bahaves_like "reservation", decrease_by: 0, has_due_date: false end context 'when user has normal role' do let(:user) { User.new(role: [:normal]) } it_bahaves_like "reservation", decrease_by: 1, has_due_date: true end end end
  26. RSpec.shared_examples 'reservation' do |decrease_by:, has_due_date:| it "reserve the book and

    decrease remaining by -#{decrease_by}" do expect { reserve_book }.to change { user.remaining_count }.by(-decrease_by) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be has_due_date end end RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new } context 'when user has admin role' do let(:user) { User.new(role: [:admin]) } it_bahaves_like "reservation", decrease_by: 0, has_due_date: false end context 'when user has normal role' do let(:user) { User.new(role: [:normal]) } it_bahaves_like "reservation", decrease_by: 1, has_due_date: true end end end Reserved DRY RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new } context 'when user has admin role' do let(:user) { User.new(role: [:admin]) } it "reserve and decrease remaining count and has no due date" do expect { reserve_book }.not_to change { user.remaining_count } expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be_falsey end end context 'when user has normal role' do let(:user) { User.new(role: [:normal]) } it "reserve and decrease remaining count and has no due date" do expect { reserve_book }.to change { user.remaining_count }.by(1) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be_truthy end end end end Better /PUGood #FUUFS ৑௕ʹॻ͍ͨ΄͏͕ඞཁͳ৚݅΍ظ଴஋͕෼͔Γ΍͍͢ RSpec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve } let(:book) { Book.new } context 'when user has admin role' do let(:user) { User.new(role: [:admin]) } it "reserve and decrease remaining count and has no due date" do expect { reserve_book }.not_to change { user.remaining_count } expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be_falsey end end context 'when user has normal role' do let(:user) { User.new(role: [:normal]) } it "reserve and decrease remaining count and has no due date" do expect { reserve_book }.to change { user.remaining_count }.by(1) expect(book.reserved_user).to eq user expect(book.reservation.due_date.present?).to be_truthy end end end end
  27. Rspec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve(user) }

    let!(:user) { User.new(remaining_count: 3) } it 'decrease remaining count by 1' do reserve_book expect(user.remaining_count).to eq 2 end end end Expect change Rspec.describe Book do describe '#reserve' do subject(:reserve_book) { book.reserve(user) } let!(:user) { User.new(remaining_count: 3) } it 'decrease remaining count by 1' do expect { reserve_book }.to change { user.remaining_count }.by(-1) end end end Better /PUGood
  28. # Case 1 RSpec.describe Book do describe '#reserve' do subject

    { book.reserve } let(:book) { Book.new(available: true) } context 'when the book is not available' do before do book.update(available: false) end # ... end end end # Case 2 RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: available, country: country) } let(:available) { true } let(:country) { :jp } context 'when the book is not available' do let(:available) { false } # ... end end end Should not update context value RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: available, country: :jp) } context 'when the book is not available' do let(:available) { false } # ... end end end Better /PUGood
  29. # Case 1 RSpec.describe Book do describe '#reserve' do subject

    { book.reserve } let(:book) { Book.new(available: true) } context 'when the book is not available' do before do book.update(available: false) end # ... end end end # Case 2 RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: available, country: country) } let(:available) { true } let(:country) { :jp } context 'when the book is not available' do let(:available) { false } # ... end end end Should not update context value RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: available, country: :jp) } context 'when the book is not available' do let(:available) { false } # ... end end end Better /PUGood /PUGood ͦͷ$POUFYUͰͷ࠷ऴతͳ஋Λཧղͮ͠Β͘ͳΔ # Case 1 RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: true) } context 'when the book is not available' do before do book.update(available: false) end # ... end end end
  30. # Case 1 RSpec.describe Book do describe '#reserve' do subject

    { book.reserve } let(:book) { Book.new(available: true) } context 'when the book is not available' do before do book.update(available: false) end # ... end end end # Case 2 RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: available, country: country) } let(:available) { true } let(:country) { :jp } context 'when the book is not available' do let(:available) { false } # ... end end end Should not update context value RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: available, country: :jp) } context 'when the book is not available' do let(:available) { false } # ... end end end Better /PUGood /PUGood MFUͰ্ॻ͖͢Δ৔߹΋ಉ͡໰୊͕ى͖Δ # Case 2 RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: available, country: country) } let(:available) { true } let(:country) { :jp } context 'when the book is not available' do let(:available) { false } # ... end end end
  31. # Case 1 RSpec.describe Book do describe '#reserve' do subject

    { book.reserve } let(:book) { Book.new(available: true) } context 'when the book is not available' do before do book.update(available: false) end # ... end end end # Case 2 RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: available, country: country) } let(:available) { true } let(:country) { :jp } context 'when the book is not available' do let(:available) { false } # ... end end end Should not update context value RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: available, country: :jp) } context 'when the book is not available' do let(:available) { false } # ... end end end Better /PUGood #FUUFS DPOUFYUͰࢦఆ͢Δ஋͸σϑΥϧτ஋Λઃఆ͠ͳ͍ RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: available, country: :jp) } context 'when the book is not available' do let(:available) { false } # ... end end end
  32. Write expectations at a high level Better /PUGood RSpec.describe Book

    do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: available) } context 'when book.available == false' do let(:available) { false } # ... end end end RSpec.describe Book do describe '#reserve' do subject { book.reserve } let(:book) { Book.new(available: available) } context 'when the book can be reserved' do let(:available) { false } # ... end end end
  33. Should not use “should” in example names • exampleͷઆ໌ʹ͸shouldΛ࢖Θͣɺݴ͍੾Δ •

    ݴ͍੾Γେࣄ • ʮόά͕͋Γ·͕ͨ͠ɺ΋͏͋Γ·ͤΜʯ by tumblr
  34. it 'is reserved' it 'should be reserved' Should not use

    “should” in example names Better /PUGood
  35. Summary * Describe and context * Use single-level contexts *

    Use let and before * Use subject and named subject * Reserved DRY * Expect change * Should not update context value * Write expectations at a high level * Should not use "should" in our example names