Learning TDD With RSpec Part 2
Checkout Kata Part 2

This is part 2 of a planned 3 part series
Introduction:
In part 1, this was achieved;
- Initialising project
- Writing a test for when nothing has been scanned
In part 2, this is going to be handled;
- Scanning of A
- Scanning of B
- Scanning of C
- Scanning of D
Tests will be run using;
⎵ + t: run entire test (⎵means the keyboardspace bar button)⎵ + l: run last test⎵ + s: run nearest test
Scanning of A:
Add another context to check for when A has been scanned. The file changes to this;
class CheckOut
def initialize(pricing_rules)
end
def total
0
end
end
RSpec.describe 'checkout' do
context 'when nothing has been scanned' do
it 'shows a total of zero' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
total = checkout.total
expect(total).to eq 0
end
end
context 'when A has been scanned' do
it 'shows the price of A' do
expect(total).to eq 50
end
end
end
Run the test by pressing ⎵ + t. We get this error;
~
./bin/rspec spec/check_out_spec.rb
Randomized with seed 34364
.F
Failures:
1) checkout when A has been scanned shows the price of A
Failure/Error: expect(total).to eq 50
NameError:
undefined local variable or method `total' for #<RSpec::ExampleGroups::Checkout::WhenAHasBeenScanned "shows the price of A" (./spec/check_out_spec.rb:22)>
# ./spec/check_out_spec.rb:24:in `block (3 levels) in <top (required)>'
Finished in 0.00256 seconds (files took 0.05539 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/check_out_spec.rb:22 # checkout when A has been scanned shows the price of A
Randomized with seed 34364
shell returned 1
Press ENTER or type command to continue
Modify the file to add the lines specifying what total is and define pricing_rules;
class CheckOut
def initialize(pricing_rules)
end
def total
0
end
end
RSpec.describe 'checkout' do
context 'when nothing has been scanned' do
it 'shows a total of zero' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
total = checkout.total
expect(total).to eq 0
end
end
context 'when A has been scanned' do
it 'shows the price of A' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
total = checkout.total
expect(total).to eq 50
end
end
end
When the test is run, as expected we get the error as shown below;
./bin/rspec spec/check_out_spec.rb
Randomized with seed 5579
.F
Failures:
1) checkout when A has been scanned shows the price of A
Failure/Error: expect(total).to eq 50
expected: 50
got: 0
(compared using ==)
# ./spec/check_out_spec.rb:27:in `block (3 levels) in <top (required)>'
Finished in 0.00758 seconds (files took 0.0523 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/check_out_spec.rb:22 # checkout when A has been scanned shows the price of A
Randomized with seed 5579
shell returned 1
Press ENTER or type command to continue
A change needs to be made to the spec to include scanning of Item A. This changes the file to be;
class CheckOut
def initialize(pricing_rules)
end
def total
0
end
end
RSpec.describe 'checkout' do
context 'when nothing has been scanned' do
it 'shows a total of zero' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
total = checkout.total
expect(total).to eq 0
end
end
context 'when A has been scanned' do
it 'shows the price of A' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
checkout.scan('A')
total = checkout.total
expect(total).to eq 50
end
end
end
When checkout.scan('A') is added, we get this error;
./bin/rspec spec/check_out_spec.rb
Randomized with seed 45438
.F
Failures:
1) checkout when A has been scanned shows the price of A
Failure/Error: checkout.scan('A')
NoMethodError:
undefined method `scan' for #<CheckOut:0x0000000130ca8448>
# ./spec/check_out_spec.rb:26:in `block (3 levels) in <top (required)>'
Finished in 0.00187 seconds (files took 0.04966 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/check_out_spec.rb:22 # checkout when A has been scanned shows the price of A
Randomized with seed 45438
shell returned 1
Press ENTER or type command to continue
Add the method: scan(item) to CheckOut class to fix this error;
class CheckOut
def initialize(pricing_rules)
end
def total
0
end
def scan(item)
end
end
Now we are back to the previous error when the test is run;
./bin/rspec spec/check_out_spec.rb
Randomized with seed 56370
.F
Failures:
1) checkout when A has been scanned shows the price of A
Failure/Error: expect(total).to eq 50
expected: 50
got: 0
(compared using ==)
# ./spec/check_out_spec.rb:33:in `block (3 levels) in <top (required)>'
Finished in 0.00768 seconds (files took 0.04974 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/check_out_spec.rb:26 # checkout when A has been scanned shows the price of A
Randomized with seed 56370
shell returned 1
Press ENTER or type command to continue
To fix this error, we make three changes to the CheckOut class as below;
class CheckOut
def initialize(pricing_rules)
@total = 0
end
def total
@total
end
def scan(item)
@total = 50
end
end
Now when the test is run, all the current two tests will pass successfully;
./bin/rspec spec/check_out_spec.rb
Randomized with seed 4857
..
Finished in 0.00201 seconds (files took 0.04948 seconds to load)
2 examples, 0 failures
Randomized with seed 4857
Press ENTER or type command to continue
Scanning of B:
Add another context to check for when B has been scanned.
Add the context to the RSpec.describe 'checkout' block.
The file changes to this;
RSpec.describe 'checkout' do
context 'when nothing has been scanned' do
it 'shows a total of zero' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
total = checkout.total
expect(total).to eq 0
end
end
context 'when A has been scanned' do
it 'shows the price of A' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
checkout.scan('A')
total = checkout.total
expect(total).to eq 50
end
end
context 'when B has been scanned' do
it 'shows the price of B' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
checkout.scan('B')
total = checkout.total
expect(total).to eq 30
end
end
end
When ⎵ + s is pressed on the keyboard to only run this new test, the following error is displayed;
./bin/rspec spec/check_out_spec.rb:36
Run options: include {:locations=>{"./spec/check_out_spec.rb"=>[36]}}
Randomized with seed 53332
F
Failures:
1) checkout when B has been scanned shows the price of B
Failure/Error: expect(total).to eq 30
expected: 30
got: 50
(compared using ==)
# ./spec/check_out_spec.rb:44:in `block (3 levels) in <top (required)>'
Finished in 0.00643 seconds (files took 0.0565 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/check_out_spec.rb:37 # checkout when B has been scanned shows the price of A
Randomized with seed 53332
shell returned 1
Press ENTER or type command to continue
To fix this, make changes to the scan method in the CheckOut class;
class CheckOut
def initialize(pricing_rules)
@total = 0
end
def total
@total
end
def scan(item)
if item == 'A'
@total = 50
elsif item == 'B'
@total = 30
end
end
end
Now when press ⎵ + l to run the last test, the test for scanning B now passes as well;
./bin/rspec spec/check_out_spec.rb:40
Run options: include {:locations=>{"./spec/check_out_spec.rb"=>[40]}}
Randomized with seed 59538
.
Finished in 0.00059 seconds (files took 0.05165 seconds to load)
1 example, 0 failures
Randomized with seed 59538
Press ENTER or type command to continue
Scanning of C:
Add another context to check for when C has been scanned.
Add the context to the RSpec.describe 'checkout' block.
This is the context;
context 'when C has been scanned' do
it 'shows the price of C' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
checkout.scan('C')
total = checkout.total
expect(total).to eq 20
end
end
When + s is pressed to run this new test, this error results;
./bin/rspec spec/check_out_spec.rb:59
Run options: include {:locations=>{"./spec/check_out_spec.rb"=>[59]}}
Randomized with seed 51710
F
Failures:
1) checkout when C has been scanned shows the price of C
Failure/Error: expect(total).to eq 20
expected: 20
got: 0
(compared using ==)
# ./spec/check_out_spec.rb:59:in `block (3 levels) in <top (required)>'
Finished in 0.00602 seconds (files took 0.05125 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/check_out_spec.rb:52 # checkout when C has been scanned shows the price of C
Randomized with seed 51710
shell returned 1
Press ENTER or type command to continue
To fix this, make changes to the CheckOut class.
Do a check for item C like so;
class CheckOut
def initialize(pricing_rules)
@total = 0
end
def total
@total
end
def scan(item)
if item == 'A'
@total = 50
elsif item == 'B'
@total = 30
elsif item == 'C'
@total = 20
end
end
end
And now when the test is run, it should be pass;
./bin/rspec spec/check_out_spec.rb:59
Run options: include {:locations=>{"./spec/check_out_spec.rb"=>[59]}}
Randomized with seed 2878
.
Finished in 0.00068 seconds (files took 0.05424 seconds to load)
1 example, 0 failures
Randomized with seed 2878
Press ENTER or type command to continue
Scanning of D:
Add another context to check for when D has been scanned.
Add the context to the RSpec.describe 'checkout' block.
This is the context;
context 'when D has been scanned' do
it 'shows the price of D' do
pricing_rules = :Rule
checkout = CheckOut.new(pricing_rules)
checkout.scan('D')
total = checkout.total
expect(total).to eq 15
end
end
When + s is pressed to run this new test, this error results;
./bin/rspec spec/check_out_spec.rb:72
Run options: include {:locations=>{"./spec/check_out_spec.rb"=>[72]}}
Randomized with seed 61671
F
Failures:
1) checkout when D has been scanned shows the price of D
Failure/Error: expect(total).to eq 15
expected: 15
got: 0
(compared using ==)
# ./spec/check_out_spec.rb:72:in `block (3 levels) in <top (required)>'
Finished in 0.00622 seconds (files took 0.0561 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/check_out_spec.rb:65 # checkout when D has been scanned shows the price of D
Randomized with seed 61671
shell returned 1
Press ENTER or type command to continue
To fix this, make changes to the CheckOut class.
Add an else to check for item D;
class CheckOut
def initialize(pricing_rules)
@total = 0
end
def total
@total
end
def scan(item)
if item == 'A'
@total = 50
elsif item == 'B'
@total = 30
elsif item == 'C'
@total = 20
else
@total = 15
end
end
end
And now when the test is run, it should be pass;
./bin/rspec spec/check_out_spec.rb:72
Run options: include {:locations=>{"./spec/check_out_spec.rb"=>[72]}}
Randomized with seed 38120
.
Finished in 0.00059 seconds (files took 0.05152 seconds to load)
1 example, 0 failures
Randomized with seed 38120
Press ENTER or type command to continue
Conclusion:
This concludes part 2.
In part 3, this will handled;
- refactoring to make the code better.
- taking the pricing rules into account
Shout out to Rob for his immense assistance on this and making it easy to comprehend.
Comments
Join the discussionNo comments yet. Be the first to comment.




