【動画付き】「プロを目指す人のためのRuby入門」のテストコードをRSpecに書き換える・その1

## はじめに

僕が執筆したRubyの入門書「[プロを目指す人のためのRuby入門](http://gihyo.jp/book/2017/978-4-7741-9397-7)」では、Minitestを使って例題の実行結果をテストしています。
ですが、実際の現場ではMinitestではなく、RSpecを使っている場合も多いと思います。

そこで、この記事では「プロを目指す人のためのRuby入門」のテストコードを、筆者自らRSpecに書き換えてみます。

[![rubybook-mini.jpg](https://qiita-image-store.s3.amazonaws.com/0/7465/975c1ed6-1815-1d08-4e3d-8fa720f1a78f.jpeg "rubybook-mini.jpg")](http://gihyo.jp/book/2017/978-4-7741-9397-7)

### RSpecをまったく知らない方へ

RSpecの基本は詳しく説明しません。RSpecをまったく知らない、という方は、先に以下の記事を読んでからこの記事に戻ってきてください。

[使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」 \- Qiita](https://qiita.com/jnchito/items/42193d066bd61c740612)
※ただし、「RSpecの高度な機能」の項で説明している知識は今回使いません。

### 本記事は3部構成になっています

ボリュームが大きいので、本記事は3部構成になっています。
この記事では第3章から第6章までのテストコードをRSpecに書き換えます。

### 動画とソースコード

この記事の内容は動画（スクリーンキャスト）形式で解説しています。
細かい説明は口頭で話しているので、ぜひ動画もチェックしてください。
（録音状況が悪いため、ときどき大きなノイズが入ります。ごめんなさい🙏）

[「プロを目指す人のためのRuby入門」のテストコードをRSpecに書き換える・その1<img width="476" alt="Screen Shot 2018-03-19 at 5.19.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/7465/d2c53054-a103-3dd0-c004-c23645ba62c7.png">](https://youtu.be/BmPD2KIZ5eY)

また、ソースコードはこちらにアップしています。

https://github.com/JunichiIto/ruby-book-codes/tree/rspec/ruby-book

### 対象バージョン

この記事は以下の環境で動作確認しています。

- Ruby 2.4.1
- RSpec 3.7.0

それでは以下が本編です。

## セットアップ

gemコマンドを使ってRSpecをインストールします。

```
$ gem install rspec
```

テストコードを書きたいプロジェクトのルートディレクトリに移動し、`rspec --init`コマンドを実行すると、RSpecの実行に必要なファイルやディレクトリが作成されます。

```
$ rspec --init
create   .rspec
create   spec/spec_helper.rb
```

`.rspec`や`spec_helper.rb`はRSpecを実行するための設定ファイルですが、今回はデフォルトのまま使うので説明は割愛します。

コンソールから`rspec`コマンドを実行し、以下のような結果になればセットアップ完了です。

```
$ rspec
No examples found.


Finished in 0.00038 seconds (files took 0.67611 seconds to load)
0 examples, 0 failures
```

セットアップ直後にはテストがありませんが、`spec`ディレクトリにテストコードがあれば、テストが実行されます。

## 第3章 FizzBuzzプログラム

### Minitest版

```ruby:test/fizz_buzz_test.rb
require 'minitest/autorun'
require './lib/fizz_buzz'

class FizzBuzzTest < Minitest::Test
  def test_fizz_buzz
    assert_equal '1', fizz_buzz(1)
    assert_equal '2', fizz_buzz(2)
    assert_equal 'Fizz', fizz_buzz(3)
    assert_equal '4', fizz_buzz(4)
    assert_equal 'Buzz', fizz_buzz(5)
    assert_equal 'Fizz', fizz_buzz(6)
    assert_equal 'Fizz Buzz', fizz_buzz(15)
  end
end
```

### RSpec版

```ruby:spec/fizz_buzz_spec.rb
require './spec/spec_helper'
require './lib/fizz_buzz'

RSpec.describe 'Fizz Buzz' do
  it 'returns valid string' do
    expect(fizz_buzz(1)).to eq '1'
    expect(fizz_buzz(2)).to eq '2'
    expect(fizz_buzz(3)).to eq 'Fizz'
    expect(fizz_buzz(4)).to eq '4'
    expect(fizz_buzz(5)).to eq 'Buzz'
    expect(fizz_buzz(6)).to eq 'Fizz'
    expect(fizz_buzz(15)).to eq 'Fizz Buzz'
  end
end
```

### ポイント

- 4行目の`RSpec.describe`では、テスト対象のプログラムやクラスを記述します。
- `it 'returns valid string' do`の部分では、プログラムの期待する振る舞いを記述しています。この場合は「（テスト対象のメソッドが）正しい文字列を返すこと」という内容になっています。
- 英語が苦手な人は`it`に渡す文字列を日本語で書いても構いません（例：`it '正しい文字列を返すこと' do`）。
- RSpecの`expect(A).to eq B`は、Minitestの`assert_equal B, A`に対応します。

## 第4章 RGB変換プログラム

### Minitest版

```ruby:test/rgb_test.rb
require 'minitest/autorun'
require './lib/rgb'

class RgbTest < Minitest::Test
  def test_to_hex
    assert_equal '#000000', to_hex(0, 0, 0)
    assert_equal '#ffffff', to_hex(255, 255, 255)
    assert_equal '#043c78', to_hex(4, 60, 120)
  end

  def test_to_ints
    assert_equal [0, 0, 0], to_ints('#000000')
    assert_equal [255, 255, 255], to_ints('#ffffff')
    assert_equal [4, 60, 120], to_ints('#043c78')
  end
end
```

### RSpec版

```ruby:spec/rgb_spec.rb
require './spec/spec_helper'
require './lib/rgb'

RSpec.describe 'RGB' do
  describe '#to_hex' do
    it 'returns valid string' do
      expect(to_hex(0, 0, 0)).to eq '#000000'
      expect(to_hex(255, 255, 255)).to eq '#ffffff'
      expect(to_hex(4, 60, 120)).to eq '#043c78'
    end
  end

  describe '#to_ints' do
    it 'returns valid array' do
      expect(to_ints('#000000')).to eq [0, 0, 0]
      expect(to_ints('#ffffff')).to eq [255, 255, 255]
      expect(to_ints('#043c78')).to eq [4, 60, 120]
    end
  end
end
```

### ポイント

- `describe '#to_hex' do`と`describe '#to_ints' do`は、それぞれテストのグループ化をしています。前者は「`to_hex`メソッドをテストするグループ」で、後者は「`to_ints`メソッドをテストするグループ」です。

## 第5章 長さの単位変換プログラム

### Minitest版

```ruby:test/convert_length_test.rb
require 'minitest/autorun'
require './lib/convert_length'

class ConvertLengthTest < Minitest::Test
  def test_convert_length
    assert_equal 39.37, convert_length(1, from: :m, to: :in)
    assert_equal 0.38, convert_length(15, from: :in, to: :m)
    assert_equal 10670.73, convert_length(35000, from: :ft, to: :m)
  end
end
```

### RSpec版

```ruby:spec/convert_length_spec.rb
require './spec/spec_helper'
require './lib/convert_length'

RSpec.describe 'Convert length' do
  it 'returns valid value' do
    expect(convert_length(1, from: :m, to: :in)).to eq 39.37
    expect(convert_length(15, from: :in, to: :m)).to eq 0.38
    expect(convert_length(35000, from: :ft, to: :m)).to eq 10670.73
  end
end
```

### ポイント

（ここでは特に新しいトピックはありません。）

## 第6章 ハッシュ記法変換プログラム

### Minitest版

```ruby:test/convert_hash_syntax_test.rb
require 'minitest/autorun'
require './lib/convert_hash_syntax'

class ConvertHashSyntaxTest < Minitest::Test
  def test_convert_hash_syntax
    old_syntax = <<~TEXT
      {
        :name => 'Alice',
        :age=>20,
        :gender  =>  :female
      }
    TEXT
    expected = <<~TEXT
      {
        name: 'Alice',
        age: 20,
        gender: :female
      }
    TEXT
    assert_equal expected, convert_hash_syntax(old_syntax)
  end
end
```

### RSpec版（letを使わない場合）

```ruby:spec/convert_hash_syntax_spec.rb
require './spec/spec_helper'
require './lib/convert_hash_syntax'

RSpec.describe 'Convert hash syntax' do
  it 'converts old syntax to new syntax' do
    old_syntax = <<~TEXT
      {
        :name => 'Alice',
        :age=>20,
        :gender  =>  :female
      }
    TEXT
    expected = <<~TEXT
      {
        name: 'Alice',
        age: 20,
        gender: :female
      }
    TEXT
    expect(convert_hash_syntax(old_syntax)).to eq expected
  end
end
```

### RSpec版（letを使う場合）

```ruby:spec/convert_hash_syntax_spec.rb
require './spec/spec_helper'
require './lib/convert_hash_syntax'

RSpec.describe 'Convert hash syntax' do
  let(:old_syntax) do
    <<~TEXT
      {
        :name => 'Alice',
        :age=>20,
        :gender  =>  :female
      }
    TEXT
  end
  let(:expected) do
    <<~TEXT
      {
        name: 'Alice',
        age: 20,
        gender: :female
      }
    TEXT
  end
  it 'converts old syntax to new syntax' do
    expect(convert_hash_syntax(old_syntax)).to eq expected
  end
end
```

### ポイント

- RSpecではテストコード内に登場するローカル変数やインスタンス変数を、`let`と呼ばれるコードブロックに移動させることができます。
- ここでは`it 'xxx' do ... end`の中身をスッキリさせるために、`let`を使いました。
- `let`については前述の「[使えるRSpec入門・その1](https://qiita.com/jnchito/items/42193d066bd61c740612)」でも説明しているので、そちらの説明も参考にしてください。

## まとめ

この記事では「[プロを目指す人のためのRuby入門](http://gihyo.jp/book/2017/978-4-7741-9397-7)」の第2章から第6章までのテストコードをRSpecに書き換えました。
第7章以降のテストコードは第2回、および第3回の記事で書き換えていきます。

- [【動画付き】「プロを目指す人のためのRuby入門」のテストコードをRSpecに書き換える・その2 \- Qiita](https://qiita.com/jnchito/items/7854dc1f4bc6eac50219)
- [【動画付き】「プロを目指す人のためのRuby入門」のテストコードをRSpecに書き換える・その3（最終回） \- Qiita](https://qiita.com/jnchito/items/54394440f91b665c870e)