Slide 1

Slide 1 text

Readable RSpec Kento Nagata / Wantedly Engineer

Slide 2

Slide 2 text

ಡΈ΍͍͢RSpecʹ͍ͭͯ
 ͓࿩Λ͍ͨ͠ͱࢥ͍·͢ɻ

Slide 3

Slide 3 text

ࣗݾ঺հ

Slide 4

Slide 4 text

Ӭా݈ਓ 8BOUFEMZ *OD8FC&OHJOFFS 8PSL 8BOUFEMZ *OD 8FC&OHJOFFS 8BOUFEMZ7JTJUͷ։ൃվળ 8BOUFEMZ5PPMTͷ։ൃվળ 7JJCBS *OD 8FC&OHJOFFS ϚονϯάαʔϏε͓Αͼ੍࡞ࢧԉπʔϧͷ։ൃ !OHULOU !OHUL

Slide 5

Slide 5 text

ͦΕͰ͸ຊ୊ʹ
 ໭Γ·͢ɻ

Slide 6

Slide 6 text

લఏ

Slide 7

Slide 7 text

RSpec 3.6.0 FactoryGirl 4.8.0

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Describe and context

Slide 10

Slide 10 text

Describe and context • describeͱcontextΛ࢖͍෼͚Δ • describe͸ςετର৅Λॻ͘ • context͸ςετ͕࣮ߦ͞ΕΔલఏ৚݅΍ঢ়ଶΛॻ͘

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Use single-level contexts

Slide 13

Slide 13 text

Use single-level contexts • ContextΛωετͤ͞ͳ͍

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

Use let and before

Slide 19

Slide 19 text

Use let and before • letͱbeforeΛ࢖ͬͯcontextΛ໌֬ʹ͢Δ • context ϒϩοΫͷ௚Լʹॻ͘ • ੜ੒ޙʹࢀর͢ΔΦϒδΣΫτ͸letΛ࢖͏ • ੜ੒ޙʹࢀর͠ͳ͍ΦϒδΣΫτ͸beforeΛ࢖͏ • Πϯελϯεม਺͸࢖Θͳ͍ • typoͯ͠΋ؾ෇͖ͮΒ͍ • `before(:each)`͸ͲͷexampleͰ΋૸ΔͨΊແବʹΞαΠϯ͞ΕΔ • ϩʔΧϧม਺ͱࠞࡏ͢ΔͱϦϑΝΫλͮ͠Β͍

Slide 20

Slide 20 text

# 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

Slide 21

Slide 21 text

# 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͕Կ͔͕Θ͔ΓͮΒ͍

Slide 22

Slide 22 text

# 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

Slide 23

Slide 23 text

# 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Λ࢖Θͳ͍

Slide 24

Slide 24 text

# 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ຖͰݺ͹Εͯ͠·͏ ϩʔΧϧม਺ͱͷ۠ผ͕ ೉͘͠ͳΔ

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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͕ରԠ͍ͯͯ͠
 ಡΈ΍͍͢

Slide 27

Slide 27 text

Use subject and named subject

Slide 28

Slide 28 text

Use subject and named subject • ςετର৅ͷΦϒδΣΫτ΍ϝιουΛsubjectͰ໌֬ʹ͢Δ • ςετର৅ʹ෭࡞༻͕͋Γɺͦͷ෭࡞༻Λςετ͢Δ৔߹
 subjectʹ໊લΛ͚ͭͱಡΈ΍͘͢ͳΔ

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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:

Slide 32

Slide 32 text

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 ओޠ͕ԿΛҙຯ͢Δͷ͔͕໌֬ʹͳΔ

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Reserved DRY

Slide 35

Slide 35 text

Reserved DRY • DRY(Don’t Repeat Yourself)͸߇͑Ίʹ͢Δ • ৑௕ੑʹΑΔมߋͷͮ͠Β͞ΑΓɺ
 ಡΈʹ͘͞ͷํ͕໰୊ʹͳΔέʔε͕ଟ͍ • shared example΍macroΛ࢖͏৔߹
 ͦͷந৅ԽʹΑͬͯಘΒΕͨ΋ͷͱՄಡੑͷόϥϯεΛߟ͑Δ΂͖

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Expect change

Slide 42

Slide 42 text

Expect change • ʮมߋʯʹ͍ͭͯςετΛ͢Δ৔߹ɺexpect changeΛ࢖͏ • ෭࡞༻ʹ͍ͭͯςετ͍ͯ͠Δ͜ͱ͕໌֬ʹͳΔ • expect changeΛ࢖Θͳ͍৔߹ɺҙਤͤͣલఏ͕յΕͯ͠·͏Մೳੑ͕͋Δ

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Should not update context value

Slide 45

Slide 45 text

Should not update context value • contextͷதͰbefore΍letͰࣄલʹఆٛͨ͠஋Λߋ৽͠ͳ͍ • ࠷ऴతͳ஋Λ೺Ѳ͢Δͷ͕೉͘͠ͳΔ

Slide 46

Slide 46 text

# 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

Slide 47

Slide 47 text

# 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

Slide 48

Slide 48 text

# 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

Slide 49

Slide 49 text

# 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

Slide 50

Slide 50 text

Write expectations at a high level

Slide 51

Slide 51 text

Write expectations at a high level • context΍exampleͷઆ໌͸ίʔυϨϕϧͷઆ໌Ͱͳ͘ɺ
 ΑΓߴڃతͳઆ໌Λॻ͘ • ίʔυϨϕϧͷઆ໌͸contextϒϩοΫͷlet΍beforeΛݟΕ͹෼͔Δ

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

Should not use “should” in example names

Slide 54

Slide 54 text

Should not use “should” in example names • exampleͷઆ໌ʹ͸shouldΛ࢖Θͣɺݴ͍੾Δ • ݴ͍੾Γେࣄ • ʮόά͕͋Γ·͕ͨ͠ɺ΋͏͋Γ·ͤΜʯ by tumblr

Slide 55

Slide 55 text

it 'is reserved' it 'should be reserved' Should not use “should” in example names Better /PUGood

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

·ͩ·ͩ͋Γ·͕͢ɺ
 Ұ൪جຊͦ͏ͳͱ͜ΖΛ
 ·ͱΊ·ͨ͠ʂ

Slide 58

Slide 58 text

օ͞Μ͕࣮ફ͍ͯ͠Δ
 ಡΈ΍͍͢ॻ͖ํ ੋඇڭ͍͑ͯͩ͘͞ʂ

Slide 59

Slide 59 text

Happy testing