rubytestingrspecgets

How to simulate multiple `gets` (user input) with RSpec


This project is a simple Ruby project, Github Repo, which I am trying to re-build using TDD, and I'm using Ruby (ruby 2.7.3) and rspec (RSpec 3.10).

My issue is, no matter how I try to simulate user input (see the commented out lines of my 'Test Code below to see what I've tried.)

Am I missing something? I feel like I don't understand this well enough, I've tried most of the suggestions I can find on Google.

I am testing the AddressChecker class, specifically the following functions:


  def handle_manual_input 
      puts "Add at least 2 Addresses, hit enter after each address, type 'quit' when done"
      while true do
        address = gets.chomp
        break if address == "quit"
        check_ethereum_address_validity(address) ? @origin_addresses << address : (puts "Error: Invalid Ethereum Address")
      end
  end

  def input_origin_addresses_manually?(y_n)  

        if y_n == "y"
      handle_manual_input
    elsif y_n == "n"
      @origin_addresses = ["0x72140C1886f8F2Dd932DCe06795901F8FB6378a7","0x0613Cd2076bd432C7A60a1b926b11B17BaAaFE11"]
    else 
      print "Please only enter 'y' or 'n' into 'input_origin_addresses_manually'"
    end
      
  end

I'm trying to test whether the output of input_origin_addresses_manually?("y") will populate @origin_addresses with more than 1 address.

When I run rspec though, I am prompted for user input (because of my address = gets.chomp within handle_manual_input.

Terminal Output from rspec:

āžœ  cryptoAddressWeb git:(main) āœ— rspec
...Add at least 2 Addresses, hit enter after each address, type 'quit' when done

and my desired input would loop like this:

...Add at least 2 Addresses, hit enter after each address, type 'quit' when done
0xa95aea385130718be87b380b419eeac8da40de55
0xa95aea385130718be87b380b419eeac8da40de55
quit
....

Test Code

    it 'If user wants to add their own addresses, check that they add more than 1 address' do
      subject.input_origin_addresses_manually?("y")

      # let(:user_input) { ["rock\n", "rock\n"] }
      # allow_any_instance_of(Object).to receive(:gets).and_return('0xa95aea385130718be87b380b419eeac8da40de55', '0xa95aea385130718be87b380b419eeac8da40de55', 'quit')
      # expect(subject).to receive(:puts).with("Add at least 2 Addresses, hit enter after each address, type 'quit' when done")
      # allow(subject).to receive(:gets).and_return('0xa95aea385130718be87b380b419eeac8da40de55', '0xa95aea385130718be87b380b419eeac8da40de55', 'quit')
      # subject.stub(gets: 'user input')
      # expect(STDOUT).to receive(:puts).with("Add at least 2 Addresses, hit enter after each address, type 'quit' when done")
      # allow(STDIN).to receive(:gets).and_return('0xa95aea385130718be87b380b419eeac8da40de55', '0xa95aea385130718be87b380b419eeac8da40de55', 'quit')
      # expect($stdout).to receive(:puts).with("Add at least 2 Addresses, hit enter after each address, type 'quit' when done")
      # allow($stdin).to receive(:gets).and_return('0xa95aea385130718be87b380b419eeac8da40de55', '0xa95aea385130718be87b380b419eeac8da40de55', 'quit')

      expect(subject.origin_addresses.length).to be > 1
    end

Would really appreciate assistance!

Thank you, Ed


Solution

  • [How to] Simulate Multiple Gets (User Input) on rspec

    class Inquisitor
      def gets_twice
        [gets, gets]
      end
    end
    
    RSpec.describe Inquisitor do
      describe '#gets_twice' do
        it 'calls gets twice' do
          inq = Inquisitor.new
          allow(inq).to receive(:gets).and_return('Apricot', 'Banana')
          expect(inq.gets_twice).to eq(%w[Apricot Banana])
        end
      end
    end