1

im trying to test a user input-influenced until loop, but am stumped on how to pull it off... i need some inspiration. how would you go about this?

class Menu
  @@menu = nil

  def get_menu
    menu = nil
    until menu == 'foo'
      menu = Readline.readline
    end
    @@menu = menu
  end
end

describe '.get_menu' do
  before do
    Readline.stub(:readline).and_return('bar')
    Menu.new.get_menu
  end

  it 'should not set class var' do
    expect(Menu.menu).to be_nil
  end
end

this is completely contrived and probably doesn't actually run, but you get the idea... (i'm using readline for tab completion, but its the same issue with gets/stdin)

a few details on gotva's solution

Since i needed to eventually set 'foo' to prevent an infinite loop, i had to test the mocks instead of the end value of Menu.menu

Readline.stub(:readline).and_return('bar', 'par', 'foo')

expect(Readline).to have_received(:readline).exactly(3).times
brewster
  • 4,342
  • 6
  • 45
  • 67

2 Answers2

1

I'd separate the tests for setting the variable and checking that the call to .readline is called the number of times for the input.

For example

class Menu                                                                                                                                                                                                 
  attr_accessor :menu                                                                                                                                                                                      

  def get_menu                                                                                                                                                                                             
    menu = nil                                                                                                                                                                                             
    menu = Readline.readline until menu == 'foo'                                                                                                                                                           
    @menu = menu                                                                                                                                                                                           
  end                                                                                                                                                                                                      
end                                                                                                                                                                                                        

describe Menu do                                                                                                                                                                                           
  subject(:menu) { Menu.new }                                                                                                                                                                              
  describe '#get_menu' do                                                                                                                                                                                  

    it 'sets instance var' do                                                                                                                                                                                 
      Readline.stub(:readline).and_return('foo')                                                                                                                                                           
      menu.get_menu                                                                                                                                                                                        
      expect(menu.menu).to eq 'foo'                                                                                                                                                                        
    end                                                                                                                                                                                                    

    it 'calls readline once until "foo" is received' do                                                                                                                                                    
      Readline.stub(:readline).and_return('foo', 'two', 'three')                                                                                                                                           
      expect(Readline).to receive(:readline).exactly(:once)                                                                                                                                                
      menu.get_menu                                                                                                                                                                                        
    end                                                                                                                                                                                                    

    it 'calls readline thrice until "foo" is received' do                                                                                                                                                  
      Readline.stub(:readline).and_return('one', 'two', 'foo')                                                                                                                                             
      expect(Readline).to receive(:readline).exactly(3).times                                                                                                                                              
      menu.get_menu                                                                                                                                                                                        
    end                                                                                                                                                                                                    
  end                                                                                                                                                                                                      
end                                                          
johnsyweb
  • 136,902
  • 23
  • 188
  • 247
0

Maybe you are looking for stub which return multiple values.

See this answer

For example

it 'should not set class var' do
  Readline.stub(:readline).and_return('bar', 'bar', 'bar')
  Menu.new.get_menu

  expect(Menu.menu).to be_nil
end

it 'should set class var' do
  Readline.stub(:readline).and_return('bar', 'bar', 'foo')
  Menu.new.get_menu

  expect(Menu.menu).to eq('foo')
end

But I see another problem: you can get infinite looping - be careful.

Community
  • 1
  • 1
gotva
  • 5,919
  • 2
  • 25
  • 35