I'm writing tests for a system that models an elevator. For example, I want to test that the elevator can change direction and that it can move to a specified floor.
I have the following methods:
def initialize
@current_floor = 0
@requested_floor = 0
end
def get_value
gets.chomp
end
def arrival
print "Enter floor number: "
@requested_floor = get_value
# only proceed if user entered an integer
if validate_floor_number(@requested_floor)
@requested_floor = @requested_floor.to_i
move
else
arrival
end
end
def move
msg = ""
@current_floor < @requested_floor ? msg = "Going Up!" : msg = "Going Down"
puts msg
@current_floor = @requested_floor
next_move
end
def next_move
puts "Do you want to go to another floor? Y/N"
another_floor = (get_value).upcase
another_floor == 'N' ? final_destination : arrival
end
I start the program by calling Elevator.new.arrival
. To check that the elevator has changed directions, I need to store the value of @current_floor
in a temporary variable then check it's value has changed after move
has been called.
I am testing input from the console using an IO pipe thanks to the answers in this question, but I'm not sure how to apply that knowledge to user interaction that's part of a method.
How can I simulate the program running from the start (Elevator.new.arrival
) through the move
method and stop it there so I can check the value of @current_floor
- all of this without running the program itself and using the IO pipe to simulate user interaction?
I have a feeling that I might have gone about the design of the program in the wrong way. If anyone can even point me in the right direction towards solving this problem I'd appreciate it.
Edit
According to the suggestions from Wand Maker I've written a test as follows:
describe "checks that the elevator can change directions" do
before do
moves = [3, 'Y', 5, 'Y', 2, 'Y', 7, 'N']
def get_value; moves.next end
end
it "should stop on floor 7" do
Elevator.new.arrival
assert_equal(@current_floor, 7)
end
end
Unfortunately, when I run my test file, the program still runs and prompts for user input. Maybe I'm calling arrival
incorrectly but I can't think of another way to do it.
As demonstrated in this answer, you can override getvalue
to feed in the user input.
Here is complete code that works without actually using gets
. I had to add couple of missing methods - validate_floor_number
and final_destination
:
require 'minitest/autorun'
class Elevator
attr_accessor :current_floor
def initialize
@current_floor = 0
@requested_floor = 0
#@last_floor = false
end
def get_value
gets.chomp
end
def validate_floor_number(v)
v.to_i rescue false
end
def arrival
print "Enter floor number: "
@requested_floor = get_value
# only proceed if user entered an integer
if validate_floor_number(@requested_floor)
@requested_floor = @requested_floor.to_i
move
else
arrival
end
end
def move
msg = ""
@current_floor < @requested_floor ? msg = "Going Up!" : msg = "Going Down"
puts msg
@current_floor = @requested_floor
next_move
end
def final_destination
puts "Reached your floor"
end
def next_move
puts "Do you want to go to another floor? Y/N"
another_floor = (get_value).upcase
another_floor == 'N' ? final_destination : arrival
end
end
describe "checks that the elevator can change directions" do
before do
class Elevator
@@moves = [3, 'Y', 5, 'Y', 2, 'Y', 7, 'N'].each
def get_value; @@moves.next end
end
end
it "should stop on floor 7" do
e = Elevator.new
e.arrival
assert_equal(e.current_floor, 7)
end
end
Output of above program:
Run options: --seed 2561
# Running:
Enter floor number: Going Up!
Do you want to go to another floor? Y/N
Enter floor number: Going Up!
Do you want to go to another floor? Y/N
Enter floor number: Going Down
Do you want to go to another floor? Y/N
Enter floor number: Going Up!
Do you want to go to another floor? Y/N
Reached your floor
.
Finished in 0.001334s, 749.4982 runs/s, 749.4982 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
[Finished in 0.3s]
This answers this part of your question: How can I simulate the program running from the start (Elevator.new.arrival) through the move method and stop it there so I can check the value of @current_floor?
- Install byebug:
gem install byebug
- Require it in your file:
require 'byebug'
- Add
byebug
command where you want to stop your program, for example at start of move
(see code at the end of the post)
- You are dropped in a shell and can examine everything, for example
@current_floor
by typing it out, or the instance by using self
- If you want to continue, hit
CTRL+D
and the program will continue (with any modification you might have done)
That should help you debugging it.
def move
byebug # <-- program will stop here
msg = ""
@current_floor < @requested_floor ? msg = "Going Up!" : msg = "Going Down"
puts msg
@current_floor = @requested_floor
next_move
end