[RSpec 3] STDOUT の output を RSpec 3 でテストする

先日、Test::Unit で STDOUT の出力をテストするコードに手間取った。

[markdown]
> * [fizzbuzz 問題を切り分けて考えるとテストが書きやすくなる | deadwood](https://www.d-wood.com/blog/2016/12/09_8670.html)

## #output.to_stdout

RSpec 3 であれば、シンプルにテストできそうなので試す。

> * [ruby – Testing STDOUT output in Rspec – Stack Overflow](http://stackoverflow.com/questions/16507067/testing-stdout-output-in-rspec)

コンソールに下記を出力するメソッドを用意する。

“`prettyprinted
1
2
Fizz
4
Buzz
FizzBuzz
“`

こんな形。

“`ruby:fizzbuzz.rb
class FizzBuzz
def foo
puts “1\n2\nFizz\n4\nBuzz\nFizzBuzz\n”
end
end
“`

これをテストするコード。

`output(***).to_stdout` だけで良い。

> * [Module: RSpec::Matchers — Documentation for rspec/rspec-expectations (master)](http://www.rubydoc.info/github/rspec/rspec-expectations/RSpec/Matchers#output-instance_method)

“`ruby:spec/fizzbuzz_spec.rb
require_relative ‘../fizzbuzz’
describe FizzBuzz do
before do
@fizz_buzz = FizzBuzz.new
end
describe ‘#foo’ do
let(:out) { “1\n2\nFizz\n4\nBuzz\nFizzBuzz\n” }
it ” do
expect { @fizz_buzz.foo }.to output(out).to_stdout
end
end
end
“`

## Test::Unit を書き換え

なので Test::Unit でこんな形で書いていたものが

“`ruby:test/test_fizzbuzz.rb
require ‘test/unit’
require ‘stringio’
require ‘./fizzbuzz’
class TestFizzBuzz < Test::Unit::TestCase def setup ARGV[0] = 15 @fizz_buzz = FizzBuzz.new @ans = [1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz', 11, 'Fizz', 13, 14, 'FizzBuzz'] end def test_console $stdout = op = StringIO.new('', 'w') result = ->(max) { @fizz_buzz.map_upto(max, @fizz_buzz.method(:fizzbuzz)) }
@fizz_buzz.console(result)
out = str2fizzbuzz_list(op.string)
assert_equal(@ans, out)
ensure
$stdout = STDOUT
end
def str2fizzbuzz_list(str)
str.split.map { |n| n =~ /(Fi|Bu)zz/ ? n : n.to_i }
end
end
“`

ひとまずこんな形で書き換えられた。

“`ruby:spec/fizzbuzz_spec.rb
require_relative ‘../fizzbuzz’
describe FizzBuzz do
before do
ARGV[0] = 15
@fizz_buzz = FizzBuzz.new
end
describe ‘#console’ do
let(:result) {
result = ->(max) { @fizz_buzz.map_upto(max, @fizz_buzz.method(:fizzbuzz)) }
@fizz_buzz.console(result)
}
let(:ans) { “1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n” }
it ” do
expect { result }.to output(ans).to_stdout
end
end
end
“`

こちらの方が後から読み返したときに分かりやすいように思います。

最終的にこうなりました(追記:2016/12/14)。

> * [training-fizzbuzz/fizz_buzz_spec.rb at feature/fizzbuzz_02 · DriftwoodJP/training-fizzbuzz](https://github.com/DriftwoodJP/training-fizzbuzz/blob/feature/fizzbuzz_02/spec/fizz_buzz_spec.rb)

## 補遺

> * [RSpecのletを使うのはどんなときか?(翻訳) – Qiita](http://qiita.com/jnchito/items/cdd9eef2ed193267c651)
[/markdown]