ひよっこエンジニアの雑多な日記

なんとかWeb系のエンジニアをやっています。

RSpecの基本的な部分を勉強してみた

ブログだいぶサボってました…。
いよいよまずいと思ったので久々に更新です。

今回はRSpecについてだらっと書きます。
Ruby on RailsでWebアプリを作る営みをしているのですが、テストをかなりおざなりにしていた+テストの書き方がイマイチわからない状態というブログ更新を滞ったよりもまずい状況だったので勉強しました。

ざっくり学んだことをもはや個人的なメモとしてだらだら書いていきます。
がっつり理解できているわけではないので内容的に曖昧なところもあるので悪しからずです…。

RSpecとはなんぞや?

Rubyのテストフレームワークの一つで、テストを書くのに特化したDSLとかいうやつみたいです。

RSpecを使うために

  • rspec --initで.rspecとspecディレクトリが作成される
  • テストを記述したファイルはspecフォルダに格納
    • xxxx_spec.rbみたいなファイル名にする
  • テスト実行はrspecコマンドを使用する
    • -fdオプションをつけるとテスト毎の成否が見れるようになる

RSpecの基本的な書き方

RSpec.describe "テスト名(クラス名とか)" do
  before do
  end
  
  it "テストケース(メソッドとか)" do
  end

  it "テストケース" 
end

beforeメソッド

  • テスト実施前の初期処理を書く(インスタンス生成とか)
  • beforeで生成した変数は変数の前に@をつけてインスタンス変数のように宣言する必要あり
  • beforeの引数には:exampleと:contextがある
    • :exampleは各exampleの前に実行される(引数省略で:exampleとなる)
    • :contextはdescribeの中の最初の一回目に実行される

example

  • it 〜 endのこと(1テストケースを指す?)
  • itはexample / specifyと書くことも可能
    • テストケースのところに書く文と文脈があうようなものを適宜選択する
  • ブロックを記載しないとpending(棚上げ)となる

describe / context

  • テスト対象を指定する(exampleのグループ化ができる)
  • describeもdescribeの中で入れ子にできる
  • トップレベルのdescribeはクラス名を書く
  • describeと同様に使うことができる
    • describeが物、contextが状況という使い分けをする

matcher

期待する振る舞いを指定する記号を指す

matcher一覧

  • eq(x):xと等しいか
  • not_eq(x):xと等しくないか
  • be [true/false]:trueかfalseか
  • be < x:xより小さいか
  • be_between(x, y).inclusive:数値がxとyの範囲内か(xとyも範囲に含む)
  • respond_to(:{メソッド名}):そのメソッドが存在しているか
  • Rubyの特徴を生かした書き方
    • 例として結果がintegerかどうか調べるテスト を書く expect(obj.add(2,3).integer?).to be true
      上記の書き方を
      expect(obj.add(2,3)).to be_integer
      と書くことができる

subject

RSpec.describe Sample do
  it {
    sample = Sample.new
    expect(sample.add(2, 3)).to eq(5)
  }
end

これを

RSpec.describe Sample do
  it {
    expect(subject.add(2, 3)).to eq(5)
  }
end

にできる。
しかし、このままだとテストケースが長くなった時にsubjectってなんだっけとなってしまうので以下のように書く。

RSpec.describe Sample do
  subject(:sample) { Sample.new }
  it {
    expect(sample.add(2, 3)).to eq(5)
  }
end

subjectにシンボルを渡して{}内でnewしてやるとsampleインスタンスとして使えるようになる。
beforeを使えば良いじゃないかという話もあるがbeforeだとexample内でインスタンス変数を使う必要が出るためsubjectの方がスッキリする。(好みの問題的な側面もあるらしいが…)

method stub

メソッドをまだ実装していないけどテストに使用したいよーという場合に使う。
ex)Userモデルから名前を取得するnameメソッドを使いたいけどまだできてないよーっていう場合

RSpec.describe Sample do
  it {
    #test doubleの呼び出し
    #doubleの引数はなくても良いが失敗した時などに便利なので書いておく
    user = double('user') 
    #nameメソッドが呼び出されたら強制的にtanakaを返すようにする
    allow(user).to receive(:name).and_return('tanaka')
  
    sample = Sample.new
    expect(sample.add(5, user.name)).to eq('5 by tanaka')
  }
end

message expectation

呼ばれなかったらテストが失敗する。
ある処理の前に確実にある処理が走っていることを保証したい場合に使用する。
ex)ログの処理を書いていなけどログを書く処理が各区実に呼ばれたかテストしたいよーっていう場合

RSpec.describe Sample do
  it {
    #ロガーのtest doubleを作成する
    logger = double('logger')
    #loggerのlogメソッドが呼ばれていることを確認する
    expect(logger).to receive(:log)
    #sampleクラスでloggerインスタンスを使用する想定
    sample = Sample.new(logger)
    expect(sample.add(5, user.name)).to eq('5 by tanaka')
  }
end

あー、久々に記事書きましたー。
文章書くのは訓練しないとどんどん書けなくなりますね…
これからは週に最低1記事を目標にブログ頑張ります!