銀行員からのRailsエンジニア

銀行員からのRailsエンジニア

銀行員から転身したサービス作りが大好きなRailsエンジニアのブログです。個人で開発したサービスをいくつか運営しており、今も新しいサービスを開発しています。転職して日々感じていること、個人開発サービス運営のことなどを等身大で書いていきます。

【技術書メモ】Everyday Rails - RSpecによるRailsテスト入門 〜毎週アウトプットチャレンジ②〜

毎週 1冊技術書を読んでブログでアウトプットするチャレンジの第二弾です〜!

 

技術書の読み方はこちらの記事に書きました。

【保存版】技術書の読み方について - 銀行員からのRailsエンジニア

「何が書いてあったか」「抽象的な考え方」を中心にアウトプットしていきます。

f:id:ysk_pro:20180722073459j:plain

第二弾で読んだ本はこちらです。

leanpub.com

RSpecについての有名な電子書籍です。

 

RSpecのセットアップ

RSpec gemをインストールすると作成されるファイルは以下の4つ
 - .rspec:設定ファイル
 - spec:スペックファイルを格納するディレクト
 - spec/spec_helper.rb・spec/rails_helper.rb:RSpecの動きをカスタマイズするヘルパーファイル
 
・.rspec ファイル内に一文追加することで、RSpec出力を読みやすいドキュメント形式にすることができる
 
・binstub をインストールすると、アプリケーションの起動時間を素早くするSpringの恩恵が受けられる
 
rails g コマンドを使った際にスペックファイルを同時に作成するための設定方法について
 

モデルスペック

・モデルはアプリケーションのコアであり、モデルが十分にテストされていると信頼性の高いコードを構築できる
 
・モデルスペックには次のテストを含める
 - 有効な値が入力された場合は、モデルの状態が有効(valid)になっていること
 - バリデーションを失敗させるデータであれば、モデルの状態が有効になっていないこと
 - クラスメソッドとインスタンスメソッドが期待通りに動作すること
 
境界値のテストをするべき
 
・describeとcontextは互換可能であるものの、基本的な使い分け方は以下の通り
 - describe:クラスやシステムの機能に関するアウトラインを記述
 - context:特定の状況に関するアウトラインを記述
 
・before はdescribeまたはcontextブロック内の各テストの前に実行される
 
可読性を優先してDRY原則に違反するのは問題ない。テストはDRYであることよりも読みやすいことの方が重要
 

テストデータの作成

・テストシナリオが複雑になった際にFactory Bot gemを使うとテストデータのセットアップをシンプルにすることができる
 
・ファクトリを使えば、FactoryBot.create(:user)と書くだけで、簡単に新しいユーザーを作成でき、スペック全体で使うことができる
 
・FactoryBot.build では新しいテストオブジェクトをメモリ内に保存し、FactoryBot.create ではアプリケーション用のデータベースにオブジェクトを永続化する
 
・ファクトリはシーケンスを使うことでそれぞれにユニークな値を設定できる
 
・ファクトリは数行のコードを書くだけで、必要なデータを作ってくれてセットアップ用のコードも短くなりテストコードが読みやすくなる。しかし、ファクトリを使うとテスト中に予期しないデータが作成されたり、無駄にテストが遅くなったりする原因にもなる可能性がある。できる限りFactoryBot.createではなく、FactoryBot.buildを使うことでデータベースに追加する回数が減り、パフォーマンスの低下が軽減できる
 

コントローラスペック

・前提として、RailsチームとRSpecチームの双方が、コントローラのテストを削除するか、モデルのテストやより高いレベルのフィーチャスペックと置き換えることを推奨している
 
・コントローラスペックは、基本的にブラウザに返すレスポンスコードをテストする
 
・Deviseのヘルパーを用いて、認証が必要な処理もテストすることができる
 

フィーチャスペックでUIをテストする

・Capybara gemを使うとリンクをクリックしたり、Webフォームを入力したり、画面の表示を検証したりすることができる
 
・コントローラスペックではユーザーインターフェースを無視して、パラメータを直接コントローラのメソッドに送信するのに対して、フィーチャスペックではユーザーが使うものと全く同じユーザーインターフェースを使ってテストをする
 
・フィーチャスペックでは1つのexample、もしくは1つのシナリオで複数のエクスペクテーションを書くのは問題ない
 
・save_and_open_page、Launchy gemを使うことで、save_and_open_pageをスペック内で呼び出した時にHTMLを自動で開き、デバッグすることができる
 
・シナリオ文の後に、js: trueというオプションを渡すことで、JavaScriptを用いたテストを行うことができる
 

リクエストスペックでAPIをテストする

API関連のテストは spec/requests ディレクトリに配置する
 
・リクエストスペックではCapybaraは使わない(Capybaraはブラウザの操作をシミュレートするだけであり、プログラム上のやりとりは特にシミュレートしないため)
  
・コントローラスペックをリクエストスペックで置き換えることは可能
 

スペックをDRYに保つ

・重複するコードはサポートモジュールに切り出すことが可能であり、フィーチャスペックでよく使われる
 
letメソッドは遅延読み込みされるため、使う必要のないデータを作成してテストが遅くなるといったことが起きにくい
 
・shared_contextを使うと、複数のテストファイルで必要なセットアップを行うことができる
 
・自分で独自のカスタムマッチャを作成することが可能
 
・aggregate_failures を使うとその中では1つのエクスペクテーションが失敗しても、次のエクスペクテーションが実行される。これにより、同じコードを何度も実行して遅くなったり、複雑なセットアップを複数のテストで共有したりせずにテストが失敗した複数のポイントを把握することができる
 

速くテストを書き、速いテストを書く

・Soulda Matchers を使えば、モデルの簡単なテストを1行で書くことができる
 
・モックは本物のオブジェクトのふりをするオブジェクトで、テストのために使われる。テストダブルと呼ばれることもある
(メリット)
 - モックはデータベースにアクセスしないため、テストにかかる時間が短くなる
 - 時間のかかるネットワーク呼び出しを実行する必要があったり、レートリミットを持つ外部APIとやりとりする必要があったりする場合、モック化はそうしたコストを最小化してくれる
 
・スタブはオブジェクトのメソッドをオーバーライドし、事前に決められた値を返す。つまり、呼び出されるとテスト用に本物の結果を返すダミーメソッド
 
・テストがとても遅くなったり、再現の難しいデータ(外部APIなど)をテストするのでなければ、無理にモックやスタブを使う必要はない
 
タグ機能を使えば、ファイル内の特定のテストだけを実行して、それ以外はスキップするようにできる。新機能追加の時などに使うとテストの実行が早くなる
 
・ParallelTests gemを使ってテストを並列に実行すると格段にスピードアップする可能性がある
 

その他のテスト

・ファイルアップロード機能のテストについて
 
・Active Jobを使ったバックグラウンドジョブのテストについて
 
・メール送信のテストについて
 

テスト駆動開発

テスト駆動開発(TDD)は、以下の手順で行うとスムーズに進む
  1.  必要なステップをテストコードにコメントとして記載する
  2.  コメントをテストコードに置き換える
  3.  テストを実行し、テストが失敗したエラーメッセージを見て、そのエラーを発生させたないための実装をする
  4.  テストが成功するまで 3. を繰り返す
 
・新機能を実装する際にテストを書くことで、リファクタリングが楽になり将来的に時間をかなり節約することができる
 

テストを書く順序

・テストを書く順序は、フィーチャスペックを書いてからモデルスペックを書く。その際はエンドユーザがタスクを完了させる手順を考えて書く。これは外から中へと呼ばれるテストを書く際の一般的なアプローチ
 

RSpecを書いてみた感想

↑テストコード書くのが楽しくなってきました!笑

 

RSpec自体は思っていたよりも難しくなく、直感的に書くことができました

 

・テストコードを実装前に考えることで、実装方法を考える時間が増え、実装がスムーズになったような気がします

 

おわりに

来週も頑張ります!(来週末引越しなので次は再来週になってしまうかもしれませんが…)

この記事で紹介した本を順番にアウトプットしていく予定ですー!

ysk-pro.hatenablog.com