inFablic | Fablic, inc. Developer's Blog.

フリマアプリ フリル (FRIL) を運営する Fablic の公式開発者ブログです。Fablic のデザイナー・エンジニア・ディレクターが情報発信していきます。

RSpec苦手な初心者が学んだ テストを書く時のコツ -- Fablic Advent Calendar 2017

こんにちは。kaeponです。

本エントリーでは、Fablicでも利用されているテストフレームワークであるRSpecについてお話しします。

最近まで私はRSpecでテストを書く事に苦手意識があり、簡単なテストしか書けなかったのですが
上司からサポートして頂き2週間程テストを書き続ける事で
自分が書きたいテストがサクサク書き始められる様になりました。

今回は沢山のテストを書き続けた中で得たTipsの一部と初心者向けのコツを紹介したいと思います。

まだまだRSpec学びたてなので基本的な内容になってしまいますが
私と同じ様にRSpecに苦手意識を持っている人や勉強を始めた方のお役に立てば幸いです。

(この記事は Fablic Advent Calender 2017 の8日目の記事です。)

テストをサクサク書き始めるためのコツ

ポイントは以下の3点です。それぞれ解説していきます。

  • 何をテストするべきかを知る
  • 最初にアウトラインを作る
  • テストのパターンを覚える

何をテストするべきかを知る

いざテストを書き始める段階で最初に考える事が「何をテストしたら良いか?」と言う事です。

例えばモデルのテストなら下記の内容を網羅するテストが書けると良いです。

  • メソッドが期待通りの出力を返すか
  • データベース等へ期待した操作を行ったか
  • バリデーションは正しく設定されているか

同様にコントローラーの場合は下記の内容を網羅します。

  • indexやshow等の各アクションがビューに渡す値が正しいか
  • アクションのステータスコードは期待通りか
  • 期待したテンプレートを選択しているか

ビューの場合は条件に応じて表示内容が期待通りであるかをテストすると良いでしょう。

学びたての頃は「これとそれのテストが必要で〜」と考えながら書いていたのですが
予めMVCの其々で何をテストするべきか知る事で余計な考え事をしなくて良くなります。

最初にアウトラインを作る

何をテストするべきか分かった所で、次はテストのアウトラインを作成します。

慣れていれば、いきなりコードを書き始めても良いのかも知れませんが
テストする箇所の規模によっては最初にアウトラインを書いてからコードを書く方が
両方の作業に集中する事が出来るのでミスや効率の面で良いと思います。

簡単な例ですが、アウトラインは下記の様に書きます。

describe ' HogeClass #fuga_method ' do
  context '入力が正しい場合' do
    it 'true が返却される' do
      # まだコードは書かない!
    end

    it 'データベースに保存される' do
    end
  end

  context '入力が正しくない場合' do
    context '◯◯が空欄の場合' do
      it 'false が返却される' do
      end
    end

    context '◯◯が△△以下の場合' do
      it 'false が返却される' do
      end
    end
  end
end

書き方のポイント

  • どのメソッドや機能をテストするかdescribeに書く
  • Aの場合を書いたら反対にBの場合も書く
  • その中で必要に応じて更に条件をcontextに書き込んでいく
  • 最終的に何が起きて欲しいかを it に書く

アウトラインを書く際には「どうやってテストコードで表現しよう?」という心配事はしなくて良いのでサクサクと作業を始める事ができます。

また、アウトラインを作成することでテストコードを書く際にも、実装箇所に集中する事が出来ます。

テストのパターンを覚える

いざ、テストを書き始めると簡単な部分(値の比較など)はスラスラと書けると思います。

一方で「これどうやってテストしよう?」と悩んでしまう難しい部分もあると思います。

こう言ったテクニックの必要な部分に手探りで挑むのは大変ですが、
一度パターンを知ってしまえば次から実装に苦労する必要はありません。

ここでは、私がテストを書く際に悩んだ部分と実装方法のパターンを紹介します。

1. 例外のテスト

メソッドやアクションが例外を返却することの検証

例えば特定の条件の時に呼ぶ事で例外が発生するメソッドを検証したい場合などに利用します

expect { hoge_method }.to raise_error(RuntimeError)

# expect(hoge_method) にすると動かないので注意が必要です。

RuntimeErrorの部分はActiveRecord::RecordNotFoundなど別な例外を指定する事も可能です。

メソッドやアクション内で例外が発生した際の挙動の検証

例外処理が書かれているコードで、実際に例外を起こした場合の挙動が正しいかを検証するのに利用します。

このテストではモックを利用しますので、モックについて詳しく知りたい方は下記が参考になると思います。

qiita.com

外から値を与えるのが難しい場合は処理の内部で使われているメソッド等にフックして
強制的に例外を発生させる必要があります。

下記の例ではhoge_methodの中でUserクラスのfindを使ってデータを取得する処理があり
その部分でActiveRecord::RecordNotFoundの例外を発生させる様にモックしています。

beforeで、このモックを設定する事によりitで実行されたメソッドの内部では意図的に例外処理が行われ返却された結果を検証出来ます。

context '例外が発生した場合' do
  before do
    allow(User).to receive(:find).and_raise(ActiveRecord::RecordNotFound)
  end

  it 'falseが返却される' do
    expect(hoge_method).to eq '例外処理の結果'
  end
end

2. Privateメソッドのテスト

Privateメソッドは通常の方法ではアクセス出来ないのでテストが出来ません。 *1

class hoge

  ~省略~

  private

  def fuga_method
    # テストしたい処理
  end
end

その場合はsendメソッドを使うことでPrivateメソッドにアクセスが出来ます。

expect(Hoge.send(:fuga_method)).to eq 1

今回はテストの中で利用しますが、sendメソッド自体はRSpecの機能ではなく
Rubyが持っている機能なのでテスト以外でも使う事が可能です。

3. 複数の項目のテスト

こちらは実装方法が分からないというよりは、手間が多い場合の対応方法です。 覚えておくと数の多い項目のテストも無駄なくスマートに記述する事が出来ます。

expect(contact.firstname).to eq ‘Jane‘
expect(contact.lastname).to eq ’Smith’
expect(contact.name).to eq ’Jane Smith

expect(contact).to have_attributes(firstname: ‘Jane‘,
                   lastname: ‘Smith‘,
                   name: ‘Jane Smith‘)

参考にした書籍

今回RSpecを勉強するにあたり、下記の書籍を参考にしました。

  • パーフェクト Ruby on Rails
  • Everyday Rails - RSpec による Rails テスト入門

おわりに

如何でしたでしょうか?

本当はもっとテストについて学んだ事が沢山あるのですが、書ききれないので今回はココまでです。

重要な部分は抑えたつもりですので誰かの役に立てば幸いです。

最初は苦手だったテストも出来る様になると、書くのが楽しくて新しい学びがあります。

みなさまも、どうか素敵なRSpecライフを!

*1:Privateメソッドのテストについては色々な議論がありますが、必要になった時の為に紹介します