紙一重の積み重ね

アラフォーのエンジニアがなれる最高の自分を目指して、学んだことをこつこつ情報発信するブログです。

【RSpec入門】はじめてRSpecのテストコードを作成して実行するまでのまとめ

ドットインストールの学習を元に、はじめてRSpecを動かしてみます。

動作環境

  • Ruby 2.3.0
  • RSpec 3.4.4
  • Cloud9

失敗するクラスを書く

サンプルのテストコードを作成する。

RSpec.describe "A calc" do
  # itで囲まれた範囲がexample
  # 期待される振る舞いのこと
  it "given 2 and 3, returns 5" do
    # この時点では存在しないクラス
    calc = Calc.new
    # 期待値:expectで記述する
    expect(calc.add(2, 3)).to eq(5)
  end
end

実行結果

Fは失敗(Failure)を表す。 期待される振る舞いが1個あって、そのうち1つが失敗している。

$ rspec
F

Failures:

  1) A calc given 2 and 3, returns 5
     Failure/Error: calc = Calc.new
     
     NameError:
       uninitialized constant Calc
     # ./spec/cals_spec.rb:4:in `block (2 levels) in <top (required)>'

Finished in 0.00096 seconds (files took 0.2081 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/cals_spec.rb:2 # A calc given 2 and 3, returns 5

仮実装を行う

上記のテストコードをパスするために仮実装を行う。 仮実装を行う理由は、テストコードの間違いがないかを確認するため。 いきなり実装をしてしまうと、テストコードが間違っている場合、テストコードと実装のどちらに誤りがあるかわからなくなる。

仮実装したクラス

class Calc
  def add(a, b)
    5 # 仮実装(テストコードの間違いがないかをまず確かめる)
  end
end

修正したテストコード

require 'calc'で仮実装したクラスを読み込む。

require 'calc'

RSpec.describe "A calc" do
  # itで囲まれた範囲がexample
  # 期待される振る舞いのこと
  it "given 2 and 3, returns 5" do
    # この時点では存在しないクラス
    calc = Calc.new
    # 期待値:expectで記述する
    expect(calc.add(2, 3)).to eq(5)
  end
end

実行結果

今度は成功する。 成功した場合は、.(ドット)が表示される。

$ rspec
.

Finished in 0.00253 seconds (files took 0.11715 seconds to load)
1 example, 0 failures

2サイクル目を回す

テストコードの追加

計算ロジックに対する、2つ目のテストを追加する。 違った角度からテストを加えることを、三角測量と呼ぶらしい。

require 'calc'

RSpec.describe "A calc" do
  # itで囲まれた範囲がexample
  # 期待される振る舞いのこと
  it "given 2 and 3, returns 5" do
    # この時点では存在しないクラス
    calc = Calc.new
    # 期待値:expectで記述する
    expect(calc.add(2, 3)).to eq(5)
  end
  # 三角測量(違った角度からテストを加えること)
  it "given 5 and 8, returns 13" do
    # この時点では存在しないクラス
    calc = Calc.new
    # 期待値:expectで記述する
    expect(calc.add(5, 8)).to eq(13)
  end
end

実行結果

2つめのテストに対する実装は行っていないためエラーとなる。 期待値が13だったのに、5がきたことが原因。

$ rspec
.F

Failures:

  1) A calc given 5 and 8, returns 13
     Failure/Error: expect(calc.add(5, 8)).to eq(13)
     
       expected: 13
            got: 5
     
       (compared using ==)
     # ./spec/cals_spec.rb:17:in `block (2 levels) in <top (required)>'

Finished in 0.03919 seconds (files took 0.13237 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./spec/cals_spec.rb:13 # A calc given 5 and 8, returns 13

処理の実装

仮実装ではなく、実際の処理を実装する。 仮実装に対して、明らかな実装と呼ぶ。

class Calc
  def add(a, b)
    # 5 # 仮実装(テストコードの間違いがないかをまず確かめる)
    a + b # 明らかな実装
  end
end

実行結果

2つのテストが成功することを確認。

$ rspec
..

Finished in 0.00141 seconds (files took 0.12859 seconds to load)
2 examples, 0 failures

リファクタリング

テストコードや実装の重複部分を取り除く。

テストコード

beforeブロックを使って、処理の共通化を行う。

require 'calc'

RSpec.describe "A calc" do
  # 共通化のためのbeforeブロック
  # :exampleで、それぞれのexample開始前に実行する
  # :contextで、最初の1回だけ実行する
  # 省略すると、:exampleになる
  before do
    # テストコード全体で参照する変数は、@をつける
    @calc = Calc.new
  end

  # itで囲まれた範囲がexample
  # 期待される振る舞いのこと
  it "given 2 and 3, returns 5" do
    # 期待値:expectで記述する
    expect(@calc.add(2, 3)).to eq(5)
  end
  # 三角測量(違った角度からテストを加えること)
  it "given 5 and 8, returns 13" do
    # 期待値:expectで記述する
    expect(@calc.add(5, 8)).to eq(13)
  end
end

リファクタリング後の実行結果

2つのテストがパスすることを確認。

$ rspec
..

Finished in 0.00145 seconds (files took 0.1221 seconds to load)
2 examples, 0 failures