Lesson 1 / Lesson 2 / Lesson 3 / Lesson 4
Review of Lesson 1
At the end of Lesson 1 you were asked to write a test for checking a guest out.
If you haven’t completed Lesson 1, go back and complete it before you proceed, or at least try. Don’t feel bad if you tried and failed to come up with a test in Lesson 1. I’m about to show you my test and how I came up with it. You’ll have more chances soon to make another attempt.
What We’ll Do In Lesson 2
In Lesson 2 I’m going to let you compare your test to mine. Then you’ll have a chance to see how I came up with the test I wrote.
Then we’ll start to add a new feature to our hotel application, room numbers.
Compare Your Test to Mine
Here’s the test (and new application code) I wrote for checking out a guest. The new code is highlighted.
require 'rspec'
class Hotel
attr_accessor :guests
def initialize
@guests = []
end
def check_in_guest(guest_name)
@guests << guest_name
end
def check_out_guest(guest_name)
@guests.delete(guest_name)
end
end
describe Hotel do
it 'can check a guest in' do
hotel = Hotel.new
hotel.check_in_guest('George Harrison')
expect(hotel.guests).to include 'George Harrison'
end
it 'can check a guest out' do
hotel = Hotel.new
hotel.check_in_guest('Buddy Holly')
hotel.check_out_guest('Buddy Holly')
expect(hotel.guests).not_to include 'Buddy Holly'
end
end
Now let me show you the steps I took to come up with this test.
How I Got Here
Before I wrote any code I asked myself, “How would I verify that a check-out feature is working?”
My answer was that I would check a guest in, then check that same guest back out. The hotel should no longer include that guest in its guest list.
Then I translated this sequence of actions—check a guest in, check a guest out, check for that guest—into code. Below is what I added.
require 'rspec'
class Hotel
attr_accessor :guests
def initialize
@guests = []
end
def check_in_guest(guest_name)
guests << guest_name
end
end
describe Hotel do
it 'can check a guest in' do
hotel = Hotel.new
hotel.check_in_guest('George Harrison')
expect(hotel.guests).to include 'George Harrison'
end
it 'can check a guest out' do
hotel = Hotel.new
hotel.check_in_guest('Buddy Holly')
hotel.check_out_guest('Buddy Holly')
expect(hotel.guests).not_to include 'Buddy Holly'
end
end
Then I ran the test, knowing it wouldn’t work. Here’s what I got.
$ rspec hotelier_spec.rb
.F
Failures:
1) Hotel can check a guest out
Failure/Error: hotel.check_out_guest('Buddy Holly')
NoMethodError:
undefined method `check_out_guest' for #<Hotel:0x007f8b3e021d60 @guests=["Buddy Holly"]>
Did you mean? check_in_guest
# ./hotelier_spec.rb:25:in `block (2 levels) in <top (required)>'
Finished in 0.00649 seconds (files took 0.13307 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./hotelier_spec.rb:22 # Hotel can check a guest out
The test of course didn’t work. The test didn’t even fail, it errored. My next step was to simply get the test to stop erroring.
require 'rspec'
class Hotel
attr_accessor :guests
def initialize
@guests = []
end
def check_in_guest(guest_name)
@guests << guest_name
end
def check_out_guest(guest_name)
end
end
describe Hotel do
it 'can check a guest in' do
hotel = Hotel.new
hotel.check_in_guest('George Harrison')
expect(hotel.guests).to include 'George Harrison'
end
it 'can check a guest out' do
hotel = Hotel.new
hotel.check_in_guest('Buddy Holly')
hotel.check_out_guest('Buddy Holly')
expect(hotel.guests).not_to include 'Buddy Holly'
end
end
Here’s what this test run looks like:
$ rspec hotelier_spec.rb
.F
Failures:
1) Hotel can check a guest out
Failure/Error: expect(hotel.guests).not_to include 'Buddy Holly'
expected ["Buddy Holly"] not to include "Buddy Holly"
# ./hotelier_spec.rb:29:in `block (2 levels) in <top (required)>'
Finished in 0.02465 seconds (files took 0.0844 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./hotelier_spec.rb:25 # Hotel can check a guest out
The test no longer errors out. It just fails.
If we were to translate this test failure to English it might be something like “I expected the list of guests not to include Buddy Holly, but it did include Buddy Holly”.
Now all that’s left to do is make the test pass.
require 'rspec'
class Hotel
attr_accessor :guests
def initialize
@guests = []
end
def check_in_guest(guest_name)
@guests << guest_name
end
def check_out_guest(guest_name)
@guests.delete(guest_name)
end
end
describe Hotel do
it 'can check a guest in' do
hotel = Hotel.new
hotel.check_in_guest('George Harrison')
expect(hotel.guests).to include 'George Harrison'
end
it 'can check a guest out' do
hotel = Hotel.new
hotel.check_in_guest('Buddy Holly')
hotel.check_out_guest('Buddy Holly')
expect(hotel.guests).not_to include 'Buddy Holly'
end
end
Now the test will pass.
$ rspec hotelier_spec.rb
..
Finished in 0.00467 seconds (files took 0.0835 seconds to load)
2 examples, 0 failures
What’s Next
So far you’ve gotten a chance to try writing your own test. Then you watched me write a test for the same feature.
Next I’m going to let you watch me develop a new feature and write tests at the same time. Then I’ll give you a chance to try writing another test of your own.
Room Numbers
In a real hotel you don’t just check into the hotel generally. You of course check into a specific room.
Let’s add some room functionality to our application.
I’m going to begin adding room functionality using coding by wishful thinking. In other words, I’ll ask myself, “What code do I wish I could use to check in a guest?” Below is what I came up with.
require 'rspec'
class Hotel
attr_accessor :guests
def initialize
@guests = []
end
def check_in_guest(guest_name)
@guests << guest_name
end
def check_out_guest(guest_name)
@guests.delete(guest_name)
end
end
describe Hotel do
it 'can check a guest in' do
hotel = Hotel.new
hotel.check_in_guest('George Harrison', 302)
expect(hotel.guests).to include 'George Harrison'
end
it 'can check a guest out' do
hotel = Hotel.new
hotel.check_in_guest('Buddy Holly')
hotel.check_out_guest('Buddy Holly')
expect(hotel.guests).not_to include 'Buddy Holly'
end
end
The `check_in_guest` method currently doesn’t take a second argument, so one of my tests will fail.
$ rspec hotelier_spec.rb
F.
Failures:
1) Hotel can check a guest in
Failure/Error:
def check_in_guest(guest_name)
@guests << guest_name
end
ArgumentError:
wrong number of arguments (given 2, expected 1)
# ./hotelier_spec.rb:10:in `check_in_guest'
# ./hotelier_spec.rb:22:in `block (2 levels) in <top (required)>'
Finished in 0.00386 seconds (files took 0.08159 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./hotelier_spec.rb:20 # Hotel can check a guest in
The way to make this test pass of course is to make the `check_in_guest` method take another argument. I’ll also have to change my other test’s use of `check_in_guest` to specify a room number.
require 'rspec'
class Hotel
attr_accessor :guests
def initialize
@guests = []
end
def check_in_guest(guest_name, room_number)
@guests << guest_name
end
def check_out_guest(guest_name)
@guests.delete(guest_name)
end
end
describe Hotel do
it 'can check a guest in' do
hotel = Hotel.new
hotel.check_in_guest('George Harrison', 302)
expect(hotel.guests).to include 'George Harrison'
end
it 'can check a guest out' do
hotel = Hotel.new
hotel.check_in_guest('Buddy Holly', 303)
hotel.check_out_guest('Buddy Holly')
expect(hotel.guests).not_to include 'Buddy Holly'
end
end
This new specifying-a-room-number thing technically works in the sense that the tests pass, but it’s a little unsatisfying. As of right now, specifying a room doesn’t actually do anything.
It seems like checking a guest into a room should do at least three things:
- Add the guest to the hotel’s guest list (we’re already testing for this)
- Disallow another guest from checking into that same room
- Decrease the total number of available rooms
We’ll address these things in Lesson 3.
Hey Jason – just finished watching the first two lessons and feel this micro course will be a great learning resource for developers new to ruby testing via RSpec. I find the examples and explanations relevant and easy to follow. I’m looking forward to following future lessons so please keep going with the great work.
Thanks Brent! Glad you found the material helpful.