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

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

#output.to_stdout

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

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

1
2
Fizz
4
Buzz
FizzBuzz

こんな形。

fizzbuzz.rb
class FizzBuzz
  def foo
    puts "1\n2\nFizz\n4\nBuzz\nFizzBuzz\n"
  end
end

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

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

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 でこんな形で書いていたものが

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

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

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)。

補遺