RSpecでRubyの標準入出力をテストするにはどうすればいいのでしょうか？

テスト対象は以下のようなコードです。

```

    class Sample
    
      def run
        puts 'Please input your name'
    
        while input = STDIN.gets.chomp
          break if input == "exit"
          puts "Your name is #{input}"
          puts 'Please input your name'
        end
      end
    end
    
    Sample.new.run
    
```

実行すると名前入力を受け付けて、入力があれば名前を表示し、また名前受付に戻る処理をループで繰り返します。exitで処理を抜けます。

```

    $ ruby test.rb
    Please input your name
    Taro
    Your name is Taro
    Please input your name
    Jiro
    Your name is Jiro
    Please input your name
    exit
    
```

このRSpecテストコードを書いているのですが無限ループに入ってしまって、どうしてもできません。

```

    describe Sample do
      describe 'run' do
    
        let(:sample) { Sample.new }
    
        it 'puts your name' do
          allow(STDIN).to receive(:gets) { 'Takashi' }
          expect(STDOUT).to receive(:puts).with('Please input your name')
          sample.run
        end
      end
    end
    
```

やりたいテストはTakashiと入れた場合はYour name is Takashiと出たら成功。  
exitと入れた場合は処理を抜ける、のテストです。

どのようにすればいいのでしょうか？
CC BY-SA 3.0
2016-09-09T11:04:25.260
28783
2016-09-09T16:49:42.327
2016-09-09T11:54:40.327
14900
14900
question
ruby-on-rails ruby rspec
RSpecでRubyの標準入出力をテストする方法
まず、test.rb の最終行を次のようにします。テスト時に `Sample.new.run` が実行されないようにするためです。

```

    Sample.new.run if $0 == __FILE__
    
```

また、今のテストコードでは `STDIN.gets` が 'Takashi' しか返しません。  
1回目に 'Takashi'、2回目に 'exit' を返すようにします。

```

    allow(STDIN).to receive(:gets).and_return 'Takashi', 'exit'
    
```

そして、`STDOUT.puts` が 'Please input your name' しか期待していないので、  
`Please input your name` や `Your name is Takashi` も受け取れるようにします。

```

    expect(STDOUT).to receive(:puts).with('Please input your name')
    expect(STDOUT).to receive(:puts).with('Your name is Takashi')
    expect(STDOUT).to receive(:puts).with('Please input your name')
    
```

これで動くと思います。
CC BY-SA 3.0
2016-09-09T16:49:42.327
28790
2016-09-09T16:49:42.327
3249
28783
answer