Integration testは、結合テストとも呼ばれています。
このテストはモデルやコントローラ等などの区切り無く、全体のテストを行います。
Integration testは、結合テストとも呼ばれています。このテストはモデルやコントローラ等などの区切り無く、全体のテストを行います。
具体的に言うと、ユーザーの動作の流れをテストします。たとえば、トップページから投稿ページに遷移し、記事を投稿して...という感じです。
このフローは、ユーザーが行うであろう動作を考えて制作しますが、ユーザーパターンが複数ある場合もあります。たとえば、上記イメージ内の右の図のように、ゲストユーザーと会員ユーザーが存在するアプリの場合です。
この場合は、それぞれのユーザーの動作ごとにテストを書きます。また、Integration testは手動でテスト用ファイルを作成する必要があります。
これまでに学んだ Unit test・Controller test は、モデル・コントローラの作成時に自動でテスト用ファイルが作られていました。しかし、Integration test は自動で作成されませんので、コマンドの実行でファイルを作成します。
integration testは、実際のユーザーの動作を想定して行うので、アプリの仕様などをしっかりと把握しておく必要があります。ですので、ここで1度アプリの仕様を確認しておきましょう。今回は、以下のような仕様のアプリとして、テストを設計していきます。
記事やコメント機能は、既に実装済みですね。ここに今回、ログイン機能を追加します。その上で、非会員・未ログインユーザーとログイン済み会員ユーザーの動作を想定してテストを書いていきます。
では、もう少し詳しくアプリの仕様を確認しておきましょう。以下は、ユーザーパターンによる機能の使用制限設定です。
仕様を確認すると、テストすべき点が見えてくるかと思います。たとえば、非会員・未ログインユーザーが記事の更新や削除を行おうとした時...というテストも書いた方が良いでしょう。
また、どのユーザーの記事なのかを判断する事も必要です。今回書くIntegration testは、この様な仕様のアプリに対して行うという想定の元制作します。
今回は、未実装の機能がまだありますので、先に機能の追加を行います。現段階で未実装なのは、ログイン機能ですね。今回は「Devise」というgemで、ログイン機能を追加します。以下手順で、追加を行なってください。
まず、ルートディレクトリ内の「Gemfile」にハイライト部分を追加します。そして、Deviseのインストールを行います。
次に、ハイライト部分を「development.rb」に追加します。これは、パスワードを忘れた方へというようなメールなどを送る時の為の設定です。このメールには、再設定用のURLの記載があります。
このURLはアプリごとに異なりますので、Deviseの方でアプリURLを取得するのですが、以下設定をしないと「https://~/user/new」という赤ハイライト部分のしか取得する事が出来ません。そうなると、エラーが発生してしまうこともあるので、以下のように設定しておきます。
次に「routes.rb」でルートURLを設定します。
そして「application.html.erb」に、ログインに成功しましたなどのアラートを表示する為のコードを追加します。Viewの生成も行います。
次に、ユーザーモデルを作成します。そして「rails g migration ~」というコマンドで、Article(記事)レコードに「user_id」というカラムを追加しています。これにより、記事と著者であるユーザーを紐づけることが出来ます。
ArticleとUserともに、モデルに関連付けの記述を行います。1人のユーザーは複数の記事を持ちますが、1つの記事は複数のユーザーを持ちません。1対多の関係です。
最後に「Articles_controller.rb」に、ユーザーパターンによる機能の使用制限や、記事作成時に「user_id」を含める為の記述を追加します。
ハイライト部分が追加する記述です。まず、非会員ユーザー・未ログインの会員ユーザーには、indexとshow以外へのアクセスは出来ないよう指定しています。
そして、create・edit・update・destroyの部分では「current_user」というメソッドが使用されています。これは、現在ログインしているユーザーの事を指します。
ですので、ログイン中ユーザーが「user_id: 1」なら「current_user.id」は「1」になります。ログインしていなければ「current_user」はnilという事になります。
このメソッドを使用して、記事投稿の際に、作成するArticleレコードの「user_id」の値としてcurrent_userの「user_id」を入れるとしています。
また「edit ・ update ・ destroy」には、もしcurrent_userが記事を投稿した本人(@article.user)ではないなら「index」にリダイレクトし「貴方は著者ではない」とメッセージを出すよう記述しています。
この記述により、著者以外が直接URLを叩いてアクセスするなどを行っても他人の記事を操作する事が出来なくなっています。これで、deviseの導入が出来ました。
ここまでで、ArticleとUserを関連付けているので、ユーザーAが投稿した記事の場合〜というテストも行う事が出来ます。ですが、既存機能と関連付けを行いましたので、既存機能やそのテストに影響が出ているかもしれません。一度テストを実行して確認してみましょう。
今まで書いたテスト全てがエラーとなっています。この原因は、usersymlを見ると分かります。
サンプルレコードは設定されていますが、値はnilの状態です。Deviseでは、emailとパスワードの値は他レコードと重複してはいけないとあらかじめ設定されています。
つまり、同じアドレスとパスワードでアカウントを作ることは出来ないと定められているのです。ちなみに、その設定は以下ファイルでされています。
ですが現在、アドレス・パスワードともにnilというレコードが2つある状態です。このため、エラーが発生しています。commentやarticleには関係ないように思えますが、このエラーが起こり問題だと伝えてきているのは、userやdeviseではなくデータベースです。
データベースとなると、commentやarticleも関係してきます。なので、今までのテストもエラーが発生しているという事です。このエラーを解消する為に「users.yml」を編集します。以下のように編集してください。
これで、nilという値を持つレコードは存在しないものになり、重複もなくなりました。では、再度テストを実行してみてください。
5つのテストはまだ成功していません。しかし、成功でない理由はエラーからテストの失敗に変わりましたね。では、テスト失敗の原因についてみていきます。
まず、editとnewです。この2つのテストに関するメッセージを見ると、新規記事投稿ページと記事の編集ページを表示するというリクエストを送り、レスポンスは要求通りのページの表示(200:success)になるべきというテスト内容であったのに、要求と異なるログインページが返された(302:Found)ということが分かります。
302:Foundは、一時的に別のページが表示されているというような意味のレスポンスです。
この失敗の原因は2つとも同じで、Articleコントローラに、indexとshow以外へのアクセス制限をかけた事が原因です。ログインしてないとアクセスできないページという風に変わった為、ログインしていない状態でそこにアクセスするとログインページに飛ばされるのです。みなさんも画面上で確認してみてください。
https://~/articles/new
この場合、アプリは想定している仕様通りに動いています。ですので、アプリ自体ではなくテストが間違っているという事になります。ですので、テストを仕様通りに変更しましょう。
newとeditはテストに成功しました。続いて、updateです。失敗理由を見ると、記事の更新に成功して、その記事の「show」ページへリダイレクトするはずなのに、ログインページへリダイレクトしているというメッセージです。
これは、記事の更新処理どうこう以前に、updateにも未ログインユーザーへの制限をかけているので、newやeditと同じように、ログインしていないのでログインページに飛ばされていることが原因です。
ですので、pathリクエストを送ったとしても記事の内容は変わらず、ログインページにリダイレクトするはず、というテストに変更します。
テスト失敗数が1つ減りました。続いて、createとdestroyです。こちらも、原因は今までのように制限をかけたことです。しかし、メッセージとしては「"Article.count" didn't change by 〇」となっています。
これは、テスト内容が作成・削除リクエストを送れば成功しデータベースの記事総数が変わるはずというテストだからです。このテスト内容は、仕様とは異なっているので変更しましょう。
これで、既存テスト及び機能の問題は大方解決できました。
ここから、Integration testの制作に入ります。まず、ファイルを作成しましょう。
このテストでは、ユーザーの動作の流れをテストするので、ログインという動作も書く必要があります。この動作はたびたび使用するので「test_helper.rb」にログインメソッドを作成しておきましょう。
ログインという動作を記述するには、上記のコードを書く必要があります。しかし、毎回このコードを書くとなると、コードがややこしくなってしまいます。
ですので「test_helper」に書いておき、更にメソッドを作成しておくことで、Integration test内では「log_in(user)」と書くだけで、ログインしたことになるのです。
そして、Integration testでは、既存記事を使用するテストも書いていきます。ですが、現在のarticle.yml内には、アプリの仕様に沿った「user_id」を含む記事がありません。ですので、以下のように編集しておきましょう。
では、Integration test ファイルを開いてください。そして、以下のようにコードを追加します。
まず「include Warden::Test::Helpers」ですが、これはDeviseでのログイン動作のテストを行う為に必要なものです。そして、setupメソッド内では2つの変数を定義しています。既存ユーザー(user_a)と、そのユーザーが投稿した記事(one)です。
次に、テスト追加します。これは、index部分のテストです。indexでは、ユーザーがトップページにアクセスして、そこにあるリンクの中のどれかをクリックすることが予想されます。ですので、indexにアクセスできること・そこに指定したリンクが存在している事をテストしています。
「assert_select」というアサ―ションは、HTMLのテストを行う為のものです。使用方法は様々ありますが、ここでは「"リンクの確認をする事", どのリンクか」という条件で使用しています。では、テストを実行します。
メッセージに表示されている行数を確認すると「new_article_path」へのリンクがないことが分かります。では、indexに記事詳細ページへのリンクなども合わせて追加します。
新記事投稿・記事詳細へのリンクの他、ログインとログアウトへのリンクも記述しました。ログイン・ログアウトに関しては、ユーザーのログイン状態を判断の上、どちらかのリンクを表示するとしています。では、テストの方にも変更を追加しましょう。
全てのテストが成功しています。ログイン動作も成功しているようです。では次に、ユーザーは新規投稿リンクをクリックしたと想定したテストを追加します。
また、ログインしていないユーザーが、新規投稿リンクをクリックした場合のテストはArticleのテストに書いてあるので、ログインしたユーザーの場合のテストとします。
テストは成功です。Article#newへのリクエスト前にログインしているので、ログインページに飛ばされることなく、新規記事投稿ページへのアクセスが成功しています。
ではさらに、記事投稿ページにはフォームがあるはずだというテストを追加します。
フォームの設置がされていないことが分かります。new.html.erbにフォームを追加しましょう。更に、edit.html.erbにもまだフォームの設置をしていませんね。この2つのフォームは、同じコードを使用します。ですので、2つのファイルに以下のようなコードを追加しましょう。
これで、newとeditにアクセスするとフォームが表示されるようになりました。では、テストを実行します。
テストは成功です。次に、無効な記事を投稿した場合のテストを追加します。ログインしていても、無効な記事は投稿できないはずです。
エラーメッセージのテスト部分では「
テストに成功しています。無効な記事の投稿は出来ず、失敗するとエラーメッセージが表示されることが保証されました。次に、有効な記事を投稿した場合のテストを追加します。
有効な記事を投稿した場合、データベースの記事総数は変動して、投稿した記事の詳細ページにリダイレクトするとしています。
そのあとに「 follow_redirect!」という記述があります。これは、記事投稿後にshowページにリダイレクトしますが、そのリダイレクト先で何かテストを行いたいという時に記述するものです。
次に、投稿できましたというメッセージが表示されるはずだとしています。最後に、記事詳細ページにはコメントフォームがあるはずだと書いています。全てのテストが成功しているので、記事の投稿についてのテストはOKです。
続いて、showページのテストを行います。showページには、編集・削除を行う為のリンクがあると良いですね。しかし著者ユーザー以外には、編集・削除ができると勘違いさせかねませんので、表示されない方が良いでしょう。
このログインの有無と、著者ユーザーであるかどうかの違いで、showページの表示内容が変わる事をテストしていきましょう。その為に、先にshowページにリンクを追加しておきます。では、テストを追加します。まず、ログインしていないユーザーの場合のテストです。
まず、ログイン動作を記述せずにshowページへのリクエストを記述します。これで、ログインしていないユーザーの場合のshowページをテストできます。
次に、戻るリンクと記事詳細が表示されている事を確認しています。どちらも「assert_select」でHTMLのテストです。記事詳細のテストの方は、spanタグの中には@article.titleの値(テキスト)が表示されているはずだという記述です。
次に、編集・削除リンクが表示されていない事を確認します。テキストリンクタグ(テキスト内容はEdit・Detroy)は、0個表示されているはずだという記述がされています。テストは成功です。次は、ログインしているユーザーの場合です。自分の記事詳細ページの場合と、他の人が投稿した記事詳細ページの場合でテストを行います。
まず、ログインして自分の記事詳細ページにアクセスしています。次に、編集・削除リンクが表示されていることを確認しています。
その後、他のユーザーの記事詳細にアクセスしています。戻るリンクと記事詳細は表示されていても、著者ではないので、編集・削除リンクは表示されないはずです。では、テストを実行します。次は、記事編集ページについてのテストです。リンクが表示されていなくても、直接URLを叩いてアクセスしようとするかもしれません。ですが、コントローラにてその行為を防ぐための記述を行っています。この記述が上手く動作するか確認します。
テストは成功です。次に、記事の編集・削除のリクエストが送られても、著者でなければ成功しないというテストを追加します。
テストは成功です。次は、著者本人の場合のedit・update・destroyを確認します。まずは、editとupdateです。
ログインしていれば、自分の記事の編集ページにアクセスできるはずです。そして、編集ページにはフォームがあるべきです。
次に、無効な値で記事を更新しようとした場合のテストを追加します。今回は、タイトルを空にして更新しようとしています。
すると、更新に失敗というメッセージが表示されるはずですし、もちろん記事の更新は出来ていないはずです。念のため、記事の状態を再読み込みして、最新の状態にしたうえで、タイトルの値を確認しています。では、テストを実行します。テストは成功です。自分の記事の更新だとしても無効な値では更新できない仕様になっています。最後に、記事更新・削除に成功した場合のテストを追加します。
テストは成功です。これで、現機能に対するIntegration test は終了です。このように、考えられるユーザーパターンを基に、それぞれのユーザーの動作を推測し書いていきます。
沢山のコードを書いてきたので大変な作業に感じられるかもしれませんが、ここまでのテストを画面上で実際に試して確認した場合と比べると、とても短時間で様々なパターンをテストできたかと思います。
テストは、ミスやバグを防ぐ事に非常に有効ですので、こまめに書きながらアプリ制作を行うようにしていきましょう。
Integration testは、結合テストとも呼ばれています。モデルやコントローラ等などの区切り無く、全体のテストを行います。
ユーザーの動作の流れをテストしましょう。ユーザーパターンが複数ある場合もあります。それぞれのユーザーの動作ごとにテストを書きます。また、Integration testは手動でテスト用ファイルを作成する必要があります。
無料ビデオ講座のお知らせ
Skillhub [スキルハブ]では無料の動画講座を多数公開しています。他校だと数万円するような講座が無料で受講できます。