Testing program that runs with user input from the

2019-08-09 08:01发布

问题:

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.

回答1:

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]


回答2:

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?

  1. Install byebug: gem install byebug
  2. Require it in your file: require 'byebug'
  3. Add byebug command where you want to stop your program, for example at start of move (see code at the end of the post)
  4. You are dropped in a shell and can examine everything, for example @current_floor by typing it out, or the instance by using self
  5. 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


标签: ruby minitest