テスト(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社エンジニアの「?」を「!」に。原著者の許諾を得て翻訳・公開いたします。GRails5中級チュートリアルはセットアップが短めで、RDBMSにはPostgreSQL、テストにはRSpecを用います。原文が非常に長いので分割します。章ごとのリンクは順次追加します。5.1.4で動作を確認しています。Railsチュートリアル(https://railstutorial.jp/)(Railsチュートリアル)とは著者も対象読者も異なります。テスト(翻訳)現時点までのアプリにはそこそこ機能が追加されています。わずかな機能であっても、アプリでテストを書いてすべてが正常に機能するための時間を使わなければならない段階に既になっています。アプリの機能が現在の20倍に増えたときのことを想像してみましょう。コードを変更するたびに、すべてが問題なく動作していることを自力でチェックするのは相当なフラストレーションです。これを避けて手動テストの時間を削減するために、テスト自動化を実装することにします。テストを書き始める前に、ここでテストについて説明します。Railsテスティングガイドも読んでRailsのテスト手法について慣れておくとよいでしょう。Climateの以下の記事もどうぞ。RSpec私がRailsアプリのテストを書き始めた頃はデフォルトのminitestを使っていましたが、今はRSpecを使っています。私はminitestとRSpecに優劣の違いはないと考えています。どちらも優れたフレームワークであり、私がRSpecを試すことに決めたのは、RailsコミュニティでRSpecの人気が高いと聞いていたからです。今ではほとんどのテストでRSpecを使っています。wayであるfixtureでサンプルデータを追加してみました。サンプルデータの選択は、テスティングフレームワークを選択するときとは違うことがわかってきました。テスティングフレームワークの選択は、おそらく個人の好みでよいと思いますが、サンプルデータについては違うと思います。fixtureは当初はよかったのですが、アプリが成長するにつれてサンプルデータの制御が難しくなりました。私の選択ミスだったようです。factoryは使った当初から何の心配もなく、実に快調です。アプリの規模が大きくても小さくても、サンプルデータを作る手間は変わりません。原文ではfactory_girlでしたが、現在はfactory_botと名前が変わっているので、本記事ではすべてfactory_girl_railsとコードをfactory_bot_rails向けに置き換えました。CapybaraCapybaraはデフォルトでrack_testドライバを使いますが、残念ながらこのドライバはJavaScriptをサポートしていません。Capybaraのデフォルトドライバの代わりに、poltergeistを使うことにします。poltergeistはJavaScriptをサポートし、ドライバの設定が私にとって最も簡単でした。spec)を楽に設定できるようになっています。今後はシステムテストがRailsの受け入れテストで主流になる可能性もあります。詳しくは以下のTechRacho記事をご覧ください。私は自分で書いたロジックをすべてテストしています。テスト対象には以下があります。ヘルパーモデルジョブデザインパターンその他自分が書いたロジックすべてロジックの他に、Capybaraでアプリの受け入れテストを書いてユーザー操作をシミュレートし、アプリの機能がすべて正常に動作することを確認するようにしています。specを使ってすべてのリクエストが正しいレスポンスを返すことを確認します。以上のテストは私のニーズに十分合うので、私の個人アプリではこれらをテストしています。当然ですが、テスト方法の標準は人それぞれ、会社ごとに異なることがあります。コントローラ/ビュー/gemのテストが含まれていない理由がおわかりでしょうか。多くのRails開発者が指摘しているように、コントローラやビューにはロジックを含めるべきではありません。これらにはテストの必要な部分がそれほどありません。コントローラやビューのテストは、ユーザーシミュレーションテスト(受け入れテスト)で十分効果を得られると私は考えています。gemは既に開発者がテストしていますし、これもシミュレーションテストで十分カバーできると思います。私ももちろん、可能な限りTDD(テスト駆動開発)アプローチを使うようにしています。TDDでは最初にテストを書き、それからコードを実装します。TDDでは開発フローがよりスムーズになりますが、最終的な機能がどんなふうに見えるか、何が出力されるかが事前にわからないこともあります。コードで実験する場合や、違う実装を試す場合は、テストを最初に書いてから実装する方法だとあまりうまくいきません。私は何らかのロジックを書く前には必ず(前述のとおり、必ずしもそうではありませんが)、独立したテストすなわち単体テストを書きます。アプリの機能がすべて正常に動作することを確認するために、Capybaraで受け入れテスト(ユーザーシミュレーションテスト)を書きます。テスト環境のセットアップ最初のテストを書く前に、テストのための環境を設定しなければなりません。Gemfileを開いて以下のgemをtestグループに追加します。gemはテスティングフレームワーク、factory_botはサンプルデータ追加用、capybaraはアプリのユーザー操作のシミュレーション用、poltergeistはテストでJavaScriptをサポートするためのドライバです。5.1以降ではCapybaraが標準で使えるのでGemfileでの追加は不要になります。5.1以降のシステムテストをRSpecで実行する(翻訳)JavaScriptをサポートする別のドライバを使ってセットアップすることもできます。gemを使う場合はPhantomJSのインストールが別途必要です。インストール方法についてはpoltergeistのドキュメントをご覧ください。headlessは、ヘッドレスドライバをサポートするのに必要です。poltergeistはヘッドレスドライバなのでこのgemが必要になります。specを使ってリクエストとレスポンスをテストするときに必要になります。gemを使うこともできます。インストール方法は以下の記事をご覧ください。gemは、テストでJavaScriptを実行した後にtestデータベースをクリーンアップするのに必要です。通常、testデータベースはテストのたびにクリーンアップされますが、JavaScriptを使う機能をテストする場合にデータベースが自動クリーンアップされないことがあります。この動作は今後変更されると思われますが、本チュートリアル執筆時点ではJavaScript実行後にtestデータベースは自動クリーンアップされません。そのため、JavaScriptの各テストの後にtestデータベースが自動クリーンアップされるようテスト環境を手動で構成しなければなりません。gemが実行されるタイミングについてはこの後設定します。gemは不要になりました。5.1以降のシステムテストをRSpecで実行する(翻訳)gemの利用目的の説明が終わりましたので、以下を実行してgemをインストールしましょう。install以下を実行して、RSpecで使うspecディレクトリを初期化します。rspec:installRSpecフレームワークでは、1つのテストを一般に「spec」と呼びます。specを実行するということは、テストを実行するということです。プロジェクトディレクトリの下にspecというディレクトリができます。テストは今後ここに書きます。他にtestというディレクトリがありますが、これはデフォルトのテスト設定の場合にテストを保存する場所です。このディレクトリは今後使わないので、削除して構いませんc(x_X)b。上述したように、JavaScriptを使うテスト向けにdatabase_cleanerのセットアップを行わなければなりません。rails_helper.rbファイルを開きます。true上の行を以下に変更します。end上のコードスニペットはこちらのチュートリアルのものを使いました。最後に、もう少し設定を行います。rails_helper.rbファイルの設定に以下を追加します(Gist)。|config|ブロックの中に書きます。:pumaコードについて少し解説します。requireメソッドによって、追加されたgemのファイルが読み込まれ、以後メソッドが使えるようになります。:feature上の設定は、capybaraテストでdeviseメソッドを使えるようにするためのものです。この設定をどこで知ったかというと、Deviseドキュメントに記載されています。gemのメソッドを使うためのものです。この設定もgemのドキュメントで見つけました。:puma上の2つの設定は、capybaraでJavaScriptをテストできるようにするために必要です。実装方法でわからない点があれば、capybaraに限らず、まずドキュメントをお読みください。テスト用gemと設定方法のほとんどを、個別ではなく一度に導入した理由は、今後問題が発生したときに備えてテストに必要なものについての全体像を明確にするためです。これで、このセクションに立ち戻ればテストの設定方法はほとんどここで確認できます。gemの導入や設定方法の記述があちこちにあると追うのが大変なのでこのセクションにまとめました。変更をcommitし、いよいよテストを書くことにしましょう。gemのドキュメントをご覧ください。どちらも内容はだいたい同じですが、多少異なる点もあります。新しいブランチを切ってそちらに切り替えます。specsまだヘルパーメソッドを1つ書いただけなので、これをテストしましょう。specディレクトリに移動して、helpersディレクトリを作成します。spec/helpersこのディレクトリにnavigation_helper_spec.rbファイルを作成します。spec/helpers/navigation_helper_spec.rbこのファイルに以下のコードを書きます(Gist)。'rails_helper'によって、すべてのテスト設定にアクセスできるようになります。spcを扱うことを指定し、これによって特定のメソッドが使えるようになります。collapsible_links_partial_pathメソッドのテストを書いたnavigation_helper_spec.rbファイルは次のようになります(Gist)。structureドキュメントをご覧ください。上では「ユーザーがログインしている場合」と「ユーザーがログインしていない場合」の2つのケースについてテストしています。userコンテキストには、それぞれbeforeフックを記述しています。これらのフック(メソッド)は、それぞれのコンテキスト内でテスト実行前に前処理を実行します。ここではテスト実行前にstubメソッドを前処理として実行し、user_signed_in?の戻り値をどんな値にもできるようにします(ここではtrueを返す)。rspeck-mocksのstubは現在非推奨です。receiveに置き換えました(参考1)(参考2)。最後に、expectメソッドを用いて、collapsible_links_partial_pathメソッドを呼び出したときの戻り値が期待どおりであることをチェックします。以下を実行すれば、すべてのテストを実行できます。specnavigation_helper_spec.rbファイルのテストだけを実行したい場合は、以下を実行します。spec/helpers/navigation_helper_spec.rbテストがパスすれば、以下のように表示されます。変更をcommitします。次に、テスト実行のためのサンプルデータが必要です。gemを使って、サンプルデータが欲しいときにいつでも簡単に追加でき、ドキュメントも充実しているのでとても快適です。この時点のアプリで作成できるオブジェクトはUserのみです。userのファクトリーを定義するために、specディレクトリにfactoriesディレクトリを作成します。spec/factoriesfactoriesディレクトリの下にusers.rbファイルを作成し、以下のコードを追加します(Gist)。endendこれで、testデータベースでのユーザー作成をspec内でいつでも簡単にfactory_botのメソッドで行えるようになりました。gemのドキュメントをご覧ください。userファクトリーの定義は割りと素直で、userオブジェクトに持たせる値を定義しています。sequenceメソッドは、ドキュメントを読むとわかるように、Userレコードを追加するたびにnの値が1つカウントアップします。最初のユーザー名はtest0、次のユーザー名はtest1という具合です。変更をcommitします。specsには、アプリでのユーザー操作をシミュレーションするテストコードを書きます。specを強化してくれます。specを書く準備はもう整っていますので、ログイン/ログアウト/サインアップ機能のテストを書くことにします。specディレクトリの下にfeaturesディレクトリを作成します。spec/featuresfeaturesディレクトリの下に、さらにuserディレクトリを作成します。spec/features/useruserディレクトリの下に、login_spec.rbファイルを作成します。spec/features/user/login_spec.rbログインテストは次のようになります。endendこのテストコードは、ユーザーがhomeページを開くとloginページに移動するところをシミュレートします。続いてフォームに入力して送信し、最後に#user-settings要素(ユーザーがログイン中のときだけ現れる)がナビゲーションバーにあることをチェックします。featureとscenarioはCapybaraの構文です。featureはcontextやdescribeと同じであり、scenarioはitと同じ機能です。Rspecをご覧ください。letを使うと、メモ化(memoize)されたメソッドを書いて、そのメソッドが定義されているcontextの内側にあるすべてのspecで利用できるようになります。letについてはRSpecのletを使うのはどんなときか?(翻訳)もどうぞ。gemのcreateメソッドも使っています。true引数を指定すると、JavaScriptに関連する機能をテストできるようになります。これまでと同様、login_spec.rbファイルを指定してテストを実行し、パスすることを確認します。spec/features/user/login_spec.rb変更をcommitします。specs"これで、ログアウト機能もテストできる状態になりました。userディレクトリの下にlogout_spec.rbファイルを作成します(Gist)。spec/features/user/logout_spec.rbテストの実装は次のようになります。endendこのコードは、ユーザーが[Logout]ボタンをクリックするところをシミュレートし、ナビゲーションバーのユーザーのリンクがログインしていない状態になることをチェックします。sign_inメソッドはDeviseのヘルパーメソッドです。したヘルパーメソッドは、既にrails_helper.rbファイルでincludeしてあります。テストを実行し、パスすることを確認します。変更をcommitします。specs"残るは、新しいアカウントでのサインアップ機能です。これをテストしましょう。userディレクトリの下にsign_up_spec.rbファイルを作成します。テストは次のようになります(Gist)。endendここではsignupページを開いてフォームへの入力と送信をシミュレートし、#user-settings要素(ログイン中のユーザーにのみ現れる)が表示されることをチェックします。createメソッドではなくDeviseのbuildメソッドを使うことで、データベースに保存せずに新しいオブジェクトを作成できます。テストスイート全体を実行し、すべてパスことを確認します。spec変更をcommitします。specs"最初のテストができあがりましたので、specsブランチをmasterにmergeします。specsspecsブランチが不要になったので削除します(q__o)。コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。チュートリアル第2版の監修および半分程度を翻訳、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れて更新翻訳中。enno.jpを運営。実は最近Go言語が好きで、Goで書かれたRubyライクなGoby言語のメンテナーでもある。仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。Storageを使ってはいけない(翻訳)#Tags#会社紹介#自社サービス#勉強会#Rails#PHP#デザイン#実績#週刊RailsウオッチCONTACTお気軽にご相談ください。(翻訳)CONTACTTechRachoでは、パートナーシップをご検討いただける方からのご連絡をお待ちしております。お気軽にご意見・ご相談ください。