- 投稿日:2020-01-14T23:26:12+09:00
ArgumentError in GroupsController#create ,wrong number of arguments (given 0, expected 1)の一例
1. どんなエラー?
「GroupsControllerのcreateの部分で引数が1つ返ってくると予想される(記述されている)のに、実行時には0件でしたよ」というエラーです
<エラーに関係したファイル>
groups_controller.rbclass GroupsController < ApplicationController def index end def new @group = Group.new @group.users << current_user end def create @group = Group.new(group_params) if @group.save redirect_to root_path, notice:'グループを作成しました' else render :new end end def edit @group = Group.find(params[:id]) end def update @group = Group.find(params[:id]) if @group.update(group_params) redirect_to root_path, notice: 'グループを更新しました' else render :edit end end private def group_params params.require.permit(:name,user_ids:[]) end end2.原因と解決方法
まず結論から言いますと、'require'の後ろにハッシュ指定がないため、'(:group)'をつけることにより解決します。
groups_controller.rbparams.require(:group).permit(:name,user_ids:[])3.原因の見つけ方
このエラーに対する正攻法として、引数の数がおかしいというエラーに対し、じゃあ〇〇行目(今回は35行目)では何を呼び出すことが可能な状態なのかを調べるという方法があります。(実践方法は下記に記載しますので必要な方はご覧いただければと思います。)
調べた内容より、間違いの確認と、呼び出したい内容がどのハッシュに格納されているか確認ができます。これにより'require'で呼び出すハッシュを決めることができます。
(※requireを使用するときは多重ハッシュとなっている場合となるため、値の直前のハッシュを指定します。これを使わないと値を格納してほしいところにハッシュ値が入ってしまいエラーとなります。今回の例がまさにそれに当たります。)
<実践方法>
①pry-rails(デバック(確認)用のツール)をインストールするため、Gemfileの最後の行に下記のコマンドを追記するgem 'pry-rails'②作業中のファイルのディレクトリで bundle installする(このコマンドがわからない方は検索をかけてもらえばいっぱい出てきます)
Neverland:chat-space-kai kontatomoya$ bundle install③指定の行を改行してその行に binding.pry を記述する(筆者の場合35行目を改行して、新しい35行目に書きます)
groups_controller.rb#1~32行目は上記の内容から変化がないので省略 33 private 34 def group_params 35 binding.pry 36 params.require.permit(:name,user_ids:[]) 37 end 38 end④rails s します
Neverland:chat-space-kai kontatomoya$ rails s⑤エラーを吐いてしまうページにつなげます。(筆者の場合は下記の写真の登録するボタンを押すとエラーページ繋がります。)すると、次のページに飛ばずに読み込み中のままとなります。
⑥この状態のままターミナルを確認します。すると下記のようになっています。
#上にもっと記述が出ますが直接関係ないので省略します From: /Users/kontatomoya/projects/chat-space-kai/app/controllers/groups_controller.rb @ line 35 GroupsController#group_params: 34: def group_params => 35: binding.pry 36: params.require.permit(:name,user_ids:[]) 37: end [1] pry(#<GroupsController>)>⑦ここで
[1] pry(#<GroupsController>)>の隣に params と打ちましょう。すると下記のような回答が返ってきます#続き [1] pry(#<GroupsController>)> params #paramsを打ちました => <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"/vrx3ZW1g+kcEgeh8V+FJlqUnBNEIR9jyvGeSl1r22IcMk+1F0I7zRhnKKXWpLNMtwjbmWxBBBaNR9phNk3KOg==", "group"=>{"name"=>"", "user_ids"=>["", "2"]}, ←この部分だけ{}で囲まれており、二重(多重)ハッシュとなっています "commit"=>"登録する", "controller"=>"groups", "action"=>"create"} permitted: false> [2] pry(#<GroupsController>)>まずこれを見ると呼び出すことができるハッシュがわかります。その中でハッシュ'group'のハッシュだけ2重ハッシュ(ActionController::Parametersに二回囲まれている状態)となっております。
今回筆者はその中の"name", "user_ids"がほしいと考えています。よって、こちらで'permit'したいハッシュ(:name,:user_ids[])の一つ上のハッシュ(今回は'group')を要求すれば良いとわかるいう流れとなります(※今回の場合そのほかの値が欲しければ'require'をつけずに'permit(:先ほど調べたハッシュ名)'とすればエラーは無くなります。)
- 投稿日:2020-01-14T23:21:37+09:00
【翻訳】Testing best practices - GitLab
GitLabのテストにおけるベストプラクティス記事を日本語訳したものです。
https://docs.gitlab.com/ee/development/testing_guide/best_practices.html
最終閲覧日時: 2020/01/06
途中、おかしな日本語があるかもしれませんので原文と照らし合わせてお読みください
Test Design
GitLabではテストは最優先事項[1]です。機能の設計と同様に、テストの設計を考慮することは重要と考えています。
機能を実装するとき、我々は適切な機能を適切な方法で開発することを検討します。これにより、範囲を管理可能なレベルに絞り込むことができます。機能のテストを実装する場合、適切なテスト実装を検討する必要がありますが、重要な箇所をすべてカバーするテストは難しく、すぐに管理困難な段階にまで拡大するでしょう。
テストヒューリスティック[2]は、この問題の解決に役立ちます。これは、我々のコードに潜むバグを明らかにし、簡潔に対処します。テストを設計する時、既知のテストヒューリスティックの確認に時間をかけ、我々のテスト設計手法を周知してください。Test Engineering sectionで有用なヒューリスティックのドキュメントが読めます。
Test speed
GitLabには大規模なテストスイートがあり、並列化しないと実行に数時間かかることがあります。正確で効果的かつ高速なテストを書く努力をすることが重要です。
テストのパフォーマンスに関する注意事項を次に示します。
doubleとspyはFactoryBot.build(...)より速いFactoryBot.build(...)と.build_stubbedは.createより速いbuild,build_stubbed,attributes_for,spy,doubleを使う時、createでオブジェクトを作成しないこと。DBの永続化は遅い。- 本当にテストする必要がある場合以外は、JavaScriptを必要とする機能のテスト(RSpecの
:jsのような)を行わないこと。ヘッドレスブラウザでのテストは遅い。RSpec
rspecでテストするには
# run all tests bundle exec rspec # run test for path bundle exec rspec spec/[path]/[to]/[spec].rbguardを使用して変更を継続的に監視し、変更されたテストのみを実行します。
bundle exec guardspringとguardを一緒に使用する場合は、代わりに
SPRING = 1 bundle exec guardとしてspringを使用してください。General guidelines
- トップレベルには
describe ClassNameを一つだけ定義することdescribe内では、クラスメソッドを.method、インスタンスメソッドを#methodと表記すること- ロジック的に分岐する場合は
contextを使うこと- テスト項目の順序はプロダクトコードクラス内の順序と一致させること
- 改行を使用してフェーズを分離し、4フェーズのテストパターンに従うこと
localhostのようなハードコーディングはせず、Gitlab.config.gitlab.hostのように設定変数を使うことsequenceによって生成された変数のような値に対してテストを行わないこと(落とし穴を参照)beforeやafterなどのフックの引数に:each(alias:example)与えてもデフォルトで効いているため引数に指定しないことbeforeとafterのフックは:allのスコープよりも:contextのスコープのほうが望ましい- 指定した要素に作用する
evaluate_script("$('.js-foo').testSomething()") (もしくはexecute_script)を使うときは、Capybaraのマッチャー(例えばfind('.js-foo'))であらかじめ要素が確実に存在することを確かめるfocus: trueを使ってテストしたい範囲を分離すること- テストに複数の期待値がある場合はaggregate_failuresを使用すること
System / Feature tests
Note: 新しいSystem / Feature testを書く前に一度、書かないことを検討してください
- feature specのファイル名は
user_changes_password_spec.rbのようにROLE_ACTION_spec.rbとすべき- シナリオタイトルには成功ケースと失敗ケースを記載すること
- 「successfully」など、情報がないようなシナリオタイトルは避けること
- 何がsuccessfullyなのか明記すること
- 機能のタイトルを繰り返すだけのシナリオタイトルは避けること
- データベースには必要なレコードのみを作成すること
- Happy path[3]とless happy pathだけをテストします。
- 可能な限り単体テストまたは統合テストでテストする必要がある
- ActiveRecord内部ではなくページに表示されるものを評価すること
- もしレコードが作成されたことを確認したかったら、
Model.count等でモデルが増えたことをテストするのではなく、その項目がページに表示されるというテストを追加すること。- DOM要素を探してもよいがテストをより脆弱にするため乱用はしないこと
Live debug
live_debugメソッド[4]を使えば、Capybaraを一時停止して、ブラウザでウェブサイトを表示できます。デフォルトブラウザで開きます。テストの実行を再開するには、任意のキーを押します。以下のような感じです。
$ bin/rspec spec/features/auto_deploy_spec.rb:34 Running via Spring preloader in process 8999 Run options: include {:locations=>{"./spec/features/auto_deploy_spec.rb"=>[34]}} Current example is paused for live debugging The current user credentials are: user2 / 12345678 Press any key to resume the execution of the example! Back to the example! . Finished in 34.51 seconds (files took 0.76702 seconds to load) 1 example, 0 failuresNote:
live_debugはJavaScriptが使える場合でのみ動きます。Run :js spec in a visible browser
以下のように
CHROME_HEADLESS=0を付けてspecを実行します。CHROME_HEADLESS=0 bundle exec rspec some_spec.rbこのテストはすぐに終わりますが、これにより何が起こっているのかわかります。
CHROME_HEADLESS=0を付けlive_debugを使って開いているブラウザを一時停止し、再び開くことができません。これは要素のデバッグと検査に使用できます。byebugまたはbinding.pryを追加して、実行を一時停止し、テストをステップ実行することもできます。
Screenshots
capybara-screenshotgemを使用して失敗時に自動的にスクリーンショットを撮ります。 CIでは、これらのファイルをジョブアーティファクトとしてダウンロードできます。また、以下のメソッドを追加することにより、テストの任意の時点でスクリーンショットを手動で取得できます。不要になったら削除してください!詳細については、ここを参照してください。
screenshot_and_save_page
- Capybaraが「見ているもの」のスクリーンショットを作成し、ページソース(htmlファイル等)を保存します。
screenshot_and_open_image
- Capybaraが「見ているも」のをスクリーンショット化し、画像を自動的に開きます。
これにより作成されたHTMLダンプにはCSSがありません。これにより、実際のアプリケーションとは大きく異なった外観になります。デバッグを容易にするCSSを追加するsmall hackがあります。
Fast unit tests
一部のクラスはRailsから十分に分離されており、RailsやBunlderの
:defaultグループのgem等によって追加されたオーバーヘッドなしでそれらをテストできるはずです。このような場合、テストファイルでspec_helperをreuqireする代わりにfast_spec_helperをreuqireできます。次の理由からテストは非常に高速に実行されるはずです。
- gemのロードをスキップ
- Railsアプリの起動をスキップ
- GitLab ShellとGitallyのスキップ
- テストリポジトリのセットアップをスキップ
fast_spec_helperはlibディレクトリ配下にある自動ロードクラスもサポートします。つまり、クラス/モジュールがlibディレクトリ配下のコードのみを使用している限り、依存関係を明示的にロードする必要はありません。fast_spec_helperは、Rails環境で一般的に使用されるコア拡張機能を含む、すべてのActiveSupport拡張機能もロードします。コードがgemを使用している場合、または依存関係がlibにない場合、require_dependencyを使用して依存関係をロードする必要があることに注意してください。
たとえば、
Gitlab::UntrustedRegexpクラスを呼び出しているコードをテストする場合は、内部でre2ライブラリを使用します。re2 gemを必要とするライブラリ内のファイルにrequire_dependency 're2'を追加して、この要件を作成する必要があります 明示的に指定するか、仕様自体に追加することもできますが、前者が優先されます。
spec_helperの場合に30秒以上かかるloadが、fast_spec_helperを使うことで1秒程度のロードですみます。let variables
GitLabのRSpecスイートでは、重複を減らすために
let(それに加えて、厳密な非遅延バージョンlet!)変数を広範囲に使用しています。しかしながらこれは時々コードを分かりにくくします。そのため、今後の使用に関するガイドラインを設定する必要があります。
let!はインスタンス変数よりも好ましい。letはlet!よりも好ましい。ローカル変数はletよりも望ましいletを使用してspecファイル全体の重複を減らせる- 単一のテストでのみ使用される変数は
letを使わずにitブロック内でローカル変数として定義すること- 最上位の記述ブロック内でlet変数を定義しないこと。これは、より深くネストされたコンテキストまたは記述ブロックでのみ使用される。定義は使用する場所にできるだけ近づけること。
- 解せない
- ある
let変数の定義を別のlet変数の定義で上書きしないようにする
- これもスコープが異なっていればいいのでは?
- 別で定義されている
let変数を定義するな(たぶん糸の異なる重複定義するなってことだと思われ)
- 代わりにヘルパーメソッドを使うべき
let!変数は定義された順序が重要な場合にのみ使用すること。それ以外はletで十分
letは遅延評価であり、参照されるまで評価されないことに注意してCommon test setup
場合によっては、各exampleでテスト用に同じオブジェクトを生成する必要はありません。たとえば、プロジェクトとのそのプロジェクトのゲストは、プロジェクトとゲストが関係するすべてのファイルに対してテストするために必要となります。これは、
test-profgemで導入できるlet_it_be変数とbefore_allフックを使うことで達成できます。let_it_be(:project) { create(:project) } let_it_be(:user) { create(:user) } before_all do project.add_guest(user) endこれにより、このcontextに対して作成されるProject, User, ProjectMemberが一つだけになります。
let_it_beおよびbefore_allは、ネストされたcontext内でも使用できます。トランザクションロールバックにより、contextが処理された後に自動的にクリーンアップされます。
let_it_beブロック内で定義されたオブジェクトを変更する場合、必要に応じてオブジェクトをリロードするか、すべての例でリロードするリロードオプションを指定する必要があることに注意してください。let_it_be(:project, reload: true) { create(:project) }また、再検索オプションを指定して、新しいオブジェクトを完全にロードすることもできます。
let_it_be(:project, refind: true) { create(:project) }set variables
Note: Gitlabでは
let_it_beを支持しているため、setを削除しつつあります。詳細はこちら参照してください。場合によっては、各exampleでテスト用に同じオブジェクトを生成する必要はありません。たとえば、プロジェクトとのそのプロジェクトのゲストは、プロジェクトとゲストが関係するすべてのファイルに対してテストするために必要となります。これは、
letを使用するのと同じ方法でsetを使用することで実現できます。
rspec-setはActiveRecordオブジェクトでのみ機能し、新しいサンプルの前に必要な場合にのみモデルをリロードまたは再作成します。つまり、プロパティを変更したときまたはオブジェクトを破棄したときです。
setブロック内のletブロックで定義されたモデルを参照することはできませんので注意してください。また、
:jsスペックでは、各exampleの後にデータベースの状態をクリーンアップするためにトランザクションを使用しないため、setはサポートされていません。Time-sensitive tests
TimecopはRubyベースで利用でき、時間依存のテストケースに有効です。時間に依存するテストを実行・検証する場合、Timecopを使用して一時的なテストの失敗を防ぎます。
例えば、
it 'is overdue' do issue = build(:issue, due_date: Date.tomorrow) Timecop.freeze(3.days.from_now) do expect(issue).to be_overdue end endFeature flags in tests
すべての機能フラグはRubyベースのテストにおいて、デフォルトで有効になるようにスタブ化されています。
テストで機能フラグを無効にするには、
stub_feature_flagsヘルパーを使用します。たとえば、テストでci_live_trace機能フラグをグローバルに無効にするには、stub_feature_flags(ci_live_trace: false) Feature.enabled?(:ci_live_trace) # => false一部のアクターに対して機能フラグを無効にし、他のアクターに対しては無効にしないテストを設定する場合、ヘルパーに渡すオプションでこれを指定できます。たとえば、特定のプロジェクトの
ci_live_trace機能フラグを無効にするには、project1, project2 = build_list(:project, 2) # Feature will only be disabled for project1 stub_feature_flags(ci_live_trace: { enabled: false, thing: project1 }) Feature.enabled?(:ci_live_trace, project1) # => false Feature.enabled?(:ci_live_trace, project2) # => truePristine test environments
1つのGitLabテストで実行されるコードは、多くのデータにアクセスして変更する場合があります。テストを実行する前に慎重に準備し、その後クリーンアップしないと、データはテストによって変更され、次のテストの動作に影響を与える可能性があります。これは必ず避けてください。幸いなことに、既存のテストフレームワークのほとんどはこのようなケースを回避しています。
テスト環境が汚染されると、一般的には不安定なテストになります。テスト環境汚染の多くの場合は、スペックAの後にスペックBを実行すると確実に失敗するが、スペックBの後にスペックAを実行すると確実に成功するといった、順序の依存関係として現れます。このような場合、
rspec --bisect5を使用して、どのスペックに問題があるかを判断できます。問題を解決するには、テストスイートで環境がどのように維持されているかをある程度理解する必要があります。続きを読んで各データストアの詳細をご覧ください。SQL database
database_cleanergemによって管理しています。各スペックはトランザクションに囲まれ、テストが完了するとロールバックされます。特定のスペックでは、完了後にすべてのテーブルに対してDELETE FROMクエリが発行されます。これにより複数のデータベース接続(ブラウザからの操作やマイグレーションスペックなどのスペックにとって重要な)から作成された行を表示できます。よく知られている
TRUNCATE TABLESアプローチの代わりにこれらの戦略を使用した結果の1つに、主キーと他のシーケンスがスペック間でリセットされないことがあります。したがって、スペックAでプロジェクトを作成してからスペックBでプロジェクトを作成すると、最初のプロジェクトはid = 1になり、2番目のプロジェクトはid = 2になります。これは、スペックがIDの値またはその他のシーケンス生成列に依存しないことを意味します。偶発的な競合を避けるため、スペックではこれらの種類の列に値を手動で指定することも避けてください。代わりに、未指定のままにして、行の作成後に値を検索します。
Redis
GitLabではRedisに、キャッシュされたデータとSidekiqジョブの2つのデータカテゴリを保存します。殆どのスペックではRailsキャッシュはメモリ内に存在しています。これはスペック間で置き換えられるため、
Rails.cache.readとRails.cache.writeの呼び出しは安全です。ただし、スペックが直接Redis呼び出しを行う場合は、必要に応じて:clean_gitlab_redis_cache、:clean_gitlab_redis_shared_state、:clean_gitlab_redis_queuestraitを適切に使用する必要があります。Background jobs / Sidekiq
デフォルトでは、Sidekiqジョブはジョブ配列にキューイングされ、処理されません。テストがSidekiqジョブをキューに入れて処理する必要がある場合は、
:sidekiq_inlinetraitを使用できます。
:sidekiq_might_not_need_inlinetraitは、Sidekiqのインラインモードがフェイクモードに変更されたときに、Sidekiqが実際にジョブを処理するのに必要なすべてのテストに追加されました。このtraitを持つテストは、Sidekiq処理ジョブに依存しないように修正するか、バックグラウンドジョブの処理が必要/予想される場合、:sidekiq_might_not_need_inlinetraitを:sidekiq_inlineに更新する必要があります。Note: ワーカーは
ApplicationJob/ActiveJob::Baseを継承していないため、perform_enqueued_jobsは現在使用できません。Filesystem
ファイルシステムのデータは、「リポジトリ」と「その他すべて」に大まかに分けることができます。リポジトリは
tmp/tests/repositoriesに保存されます。このディレクトリは、テストが実行される前、およびテストが終了した後に空になります。スペック間では空にならないため、作成されたリポジトリはプロセスの存続期間中、このディレクトリ内に蓄積されます。それらを削除するのはコストがかかりますが、注意深く管理しないと汚染につながる可能性があります。これらを回避するには、テストスイートでハッシュストレージを有効にします。つまり、リポジトリにはプロジェクトのIDに依存する一意のパスが与えられます。プロジェクトIDはスペック間でリセットされないため、各スペックがディスク上の独自のリポジトリを取得することが保証され、スペック間で変更が表示されないようにします。
スペックでプロジェクトIDを手動で指定する場合、または
tmp/tests/repositories/ディレクトリの状態を直接検査する場合、実行の前後にディレクトリをクリーンアップする必要があります。一般的に、これらのパターンは完全に回避する必要があります。アップロードなど、データベースオブジェクトにリンクされた他のクラスのファイルは、通常同じ方法で管理されます。スペックでハッシュストレージが有効になっている場合、IDによって決定される場所のディスクに書き込まれるため、競合は発生しません。
一部のスペックでは、
projectsfactoryに:legacy_storagetraitを渡すことで、ハッシュストレージを無効にします。これを行うスペックは、プロジェクトまたはそのグループのパスをオーバーライドしてはなりません。デフォルトのパスにはプロジェクトIDが含まれているため、競合しません。ただし、2つの仕様が同じパスを持つ:legacy_storageプロジェクトを作成する場合、ディスク上の同じリポジトリを使用し、環境汚染をテストします。その他のファイルは、スペックによって手動で管理する必要があります。たとえば、
tmp/test-file.csvファイルを作成するコードを実行する場合、スペックでは、クリーンアップの一環としてファイルが削除されるようにする必要があります。Persistent in-memory application state
Rspecによるスペックはすべて同じRubyプロセスを共有します。つまり、スペック間でアクセス可能なRubyオブジェクトを変更することで、互いに影響を与えることができます。これはグローバル変数、および定数(クラス、モジュールなどを含む)であることを意味しています。
通常、グローバル変数は変更しないでください。どうしても必要な場合、以下のようなブロックを使用して、変更を後でロールバックできます。
around(:each) do |example| old_value = $0 begin $0 = "new-value" example.run ensure $0 = old_value end endスペックで定数を変更する必要がある場合は、
stub_constヘルパーを使用して、変更が確実にロールバックされるようにする必要があります。
ENV定数を変更する必要がある場合は、代わりにstub_envヘルパーメソッドを使用できます。ほとんどのRubyインスタンスはスペック感で共有されませんが、クラスとモジュールは一般的に共有されます。クラスおよびモジュールのインスタンス変数、アクセサー、クラス変数、およびその他のステートフルイディオムはグローバル変数と同じように扱われるべきです。必要がない限り変更しないでください。とくに、変更の必要性を排除するために、expectまたはstubに沿った依存関係の代入かを使うのことが望ましいです。他に選択肢がない場合は、上記のグローバル変数と同様に
aroundブロックが使用できますが、可能な限り回避する必要があります。Table-based / Parameterized tests
このスタイルのテストは、包括的な入力範囲で1つのコードを実行するために使用されます。テストケースを1回指定するだけで、入力のテーブルとそれぞれの予想出力とともに、テストを読みやすく、コンパクトにすることができます。
GitLabではrspec-parameterizedgemを使っています。テーブル構文を使用し、Rubyの入力範囲をチェックする短い例は、次のようになります。
describe "#==" do using RSpec::Parameterized::TableSyntax where(:a, :b, :result) do 1 | 1 | true 1 | 2 | false true | true | true true | false | false end with_them do it { expect(a == b).to eq(result) } it 'is isomorphic' do expect(b == a).to eq(result) end end endCaution: whereブロックの入力として単純な値のみを使用します。プロシージャ、ステートフルオブジェクト、FactoryBotで作成されたオブジェクトなどを使用すると、予期しない結果が生じる可能性があります。
Prometheus tests
Prometheusメトリクス[6]は、テストの実行ごとに保持される場合があります。各サンプルの前にメトリクスが確実にリセットされるようにするには、Rspecテストに
:prometheusタグを追加します。Matchers
カスタムマッチャーを作成して、意図を明確にし、RSpecの予想の複雑さを隠す必要があります。これは
spec/support/matchers/に配置する必要があります。マッチャーは、特定のタイプのスペック(機能スペック、リクエストスペックなど)にのみ適用される場合はサブフォルダーに配置できますが、複数のタイプの仕様に適用する場合は配置しないでください。be_like_time
データベースから返される時間は、Rubyの時間オブジェクトと精度が異なる場合があります。そのため、スペックを比較する際に柔軟な許容範囲が必要です。
be_like_timeを使用して、時間が1秒以内であることを比較できます。expect(metrics.merged_at).to be_like_time(time)have_gitlab_http_status
have_http_statusよりもhave_gitlab_http_statusをお勧めします。have_gitlab_http_statusは、ステータスが一致しない場合に常に応答本文も表示できるためです。これは、テストが落ちたときにソースコードを編集せず、テストを再実行せずとも落ちた原因を知るのに非常に役に立ちます。特に500サーバーエラーが表示されている場合に便利です。
Shared contexts
すべての
shared contextは、spec/support/shared_contexts/に配置する必要があります。shared contextは、特定のタイプのスペック(機能スペック、リクエストスペックなど)にのみ適用される場合サブフォルダーに配置できますが、複数のタイプの仕様に適用される場合はそうではありません。各ファイルにはコンテキストが1つだけ含まれ、わかりやすい名前を付ける必要があります。
(e.g.spec/support/shared_contexts/controllers/githubish_import_controller_shared_context.rb.)Shared examples
すべての
shared exampleは、spec/support/shared_exampless/に配置する必要があります。shared examplesは、特定のタイプのスペック(機能スペック、リクエストスペックなど)にのみ適用される場合サブフォルダーに配置できますが、複数のタイプの仕様に適用される場合はそうではありません。各ファイルにはコンテキストが1つだけ含まれ、わかりやすい名前を付ける必要があります。
(e.g.spec/support/shared_exampless/controllers/githubish_import_controller_shared_example.rb.)Helpers
ヘルパーは通常、特定のRSpecのexampleの複雑さを隠すためのメソッドを提供するモジュールです。他のスペックと共有することを意図していない場合、RSpecファイルでヘルパーを定義できます。それ以外の場合は、
spec/support/helpers/に配置する必要があります。特定のタイプのスペック(機能スペック、リクエストスペックなど)のみに適用される場合、ヘルパーはサブフォルダーに配置できます。
ヘルパーはRailsの命名規則/名前空間規則に従う必要があります。たとえば、
spec/support/helpers/cycle_analytics_helpers.rbは以下のように定義する必要があります。module Spec module Support module Helpers module CycleAnalyticsHelpers def create_commit_referencing_issue(issue, branch_name: random_git_name) project.repository.add_branch(user, branch_name, 'master') create_commit("Commit for ##{issue.iid}", issue.project, user, branch_name) end end end end endヘルパーでRSpecの設定を変更しないでください。たとえば、上記のヘルパーモジュールには以下を含めないでください。
RSpec.configure do |config| config.include Spec::Support::Helpers::CycleAnalyticsHelpers endFactories
GitLabはテスト用のFixture[7]の代替としてfactory_botを使用します。
- Factoryは
spec/factories/で定義し、対応するモデルの複数形を使用して命名します(Userのfactoryはusers.rb)。- ファイルごとにトップレベルのファクトリ定義は1つだけにする必要があります。
- FactoryBotメソッドは、すべてのRSpecグループに混在しています。つまり
Factory.create(...)の代わりにcreate(...)を呼び出すことができます(そして呼び出す必要があります)。- traitを使用して定義と使用方法をクリーンアップします。
- ファクトリを定義するとき、モデルに関係のないカラムを定義しないでください。
- ファクトリをインスタンス化するときは、不要なカラムを指定しないでください。
- ファクトリはActiveRecordオブジェクトに限定される必要はありません。例を参照してください。
Fixtures
すべてのFixtureは
spec/fixtures/の下に配置する必要があります。Repositories
マージリクエストのマージなどの一部の機能をテストするには、特定の状態のGitリポジトリがテスト環境に存在する必要があります。GitLabは、特定の一般的なケースに対してgitlab-testリポジトリを維持します。プロジェクトファクトリの
:repositoryトレイトでリポジトリのコピーが使用されていることを確認できますlet(:project) { create(:project, :repository) }可能な場合は、
:repositoryではなく:custom_repoトレイトの使用を検討してください。これにより、プロジェクトのリポジトリのmasterブランチに表示されるファイルを正確に指定できます。let(:project) do create( :project, :custom_repo, files: { 'README.md' => 'Content here', 'foo/bar/baz.txt' => 'More content here' } ) endこれにより、デフォルトの権限と指定されたコンテンツを持つ2つのファイルを含むリポジトリが作成されます。
Config
RSpecの設定ファイルは、RSpecのコンフィグ(すなわち
RSpec.configure do |config|ブロック)を変更するファイルです。これはspec/support/に配置する必要があります。各ファイルには特定のドメインに関連する必要があります。たとえば、
spec/support/capybara.rb,spec/support/carriewave.rbなどです。ヘルパーモジュールが特定の種類のスペックにのみ適用される場合、
config.include呼び出しに修飾子を追加する必要があります。たとえば、spec/support/helpers/cycle_analytics_helpers.rbが:libおよびtype: :modelスペックにのみ適用される場合、次のように記述します。RSpec.configure do |config| config.include Spec::Support::Helpers::CycleAnalyticsHelpers, :lib config.include Spec::Support::Helpers::CycleAnalyticsHelpers, type: :model end構成ファイルが
config.includeのみで構成されている場合、これらのconfig.includeをspec/spec_helper.rbに直接追加できます。汎用的なヘルパーについては、
spec/fast_spec_helper.rbファイルで使用されるspec/support/rspec.rbファイルに含めることを検討してください。spec/fast_spec_helper.rbファイルの詳細については、高速ユニットテストを参照してください。注釈
- [1] first class citizenの訳がふわっとしていた(一級市民 or 第一級オブジェクト)のでこの記事をみて「最優先事項」との訳にした
- [2] ヒューリスティック評価:経験則(ヒューリスティックス)に基づいてユーザビリティを評価し、UI上の問題を発見する手法。ここでは「テストにおける経験則・ナレッジ」の意味合いに近い
- [3] 最も使用頻度の高いユースケースのテストをハッピーテストっていうんですね。知らなんだ...
- [4]
live_debugなんてメソッドCapybaraにもRspecにもいないぞと思ったらこういうことでした- [5] rspec --bisect
- [6] https://qiita.com/sugitak/items/ff8f5ad845283c5915d2
- [7] 初期データを投入する(https://qiita.com/itkrt2y/items/ca34fea17fc7dde56b7a)
- 投稿日:2020-01-14T22:01:53+09:00
お名前.comを使い、独自ドメイン取得し、herokuデプロイする
こんにちは。
初学者の方で転職活動などの為にポートフォリオを作成している方も多いと思います。その際にherokuを使ってデプロイする人が多いと思うのですが、
URLが、
〜herokuapp.comとなってしまい、なんかカッコ悪いなあって思いまして、
AWSを使って色々やろうとしたのですが、ポートフォリオだからそこまで口数をかけたくないと思い
独自ドメインを使い、herokuで、デプロイすることにしました。
その手順を載せさせていただきます。
初学者の方への参考になれたら幸いです。※前提として
Herokuにアプリをデプロイ済みであることとします1.お名前.comで独自ドメインを取得する
にて
取得したいドメイン名を入れて検索する追加できるものを探し、
個人情報、クレジット情報を入力する
ここでは
test.work
を取得したことにして進めます。2.herokuの設定
herokuはhobbyプラン(無料の一つ上の7$のプラン)以上じゃないと独自ドメインが使えないためです。
ちなみにこちらは
7$/月の料金がかかってしまいます。Herokuのプランはアカウントごとではなく、アプリケーションごとに設定します。例えば、2つのアプリケーションをHerokuにデプロイしている場合、それぞれのアプリケーションに対して無料・有料プランにするかどうかを設定することになります。
herokuをhobbyプランにする
https://dashboard.heroku.com/apps
より、
デプロイするアプリを押下、詳細画面で、
Resources > [Change Dyno Type]を押下し、
[Hobby]を選択し[save]を押下します。heroku側のドメイン設定とSSL設定
[Settings]を押下し、[Add domain]を押下します。
お名前.comで取得したドメインに
www.
を追加し入力し、[Next]を押下します。DNS target
が表示されるので、コピーします。次に[Configure SSL]を押下し、
Automatic Certificate Management (ACM)
を選択し、[Next]を押下します。
確認画面になるので、もう一度[Next]を押下します。
最後に[Finish]を押下します。
3.お名前.comの設定
再びお名前.comにいき、
[ドメイン設定]を押下、
[DNS関連機能の設定]を押下、
[DNS設定/転送設定]を押下します。TYPE→CNAME
ホスト名→wwwを入力
VALUEには、先程herokuでコピーしたDNS Targetを貼り付け
[追加]を押下します。画面下部まで進み確認へ進み、
[確認内容へ進む]を押下します。これで、設定は完了です!!
※UI上でうまくいかない場合は
コマンドでの設定をおすすめします
こちらを参考に!
https://medium.com/@kjmczk/heroku-cdomain-ssl-1b4cae424e61反映まで数時間かかるようなので、
メールが来るまで待ちましょう!!ドメインが反映される前に取得したドメインにアクセスするとお名前.comのページに飛ばされ『このドメインは取得されています』と表示される思いますが、心配せず気長に待ってください。
24〜72時間の間でドメインが反映されるらしいのですが、
自分の場合は数十分で
反映されました!無事に公開されました!!
- 投稿日:2020-01-14T21:45:55+09:00
Rails エラーメッセージの表示と日本語化
エラーメッセージの表示
上記の画像のようにタイトルとブログ本文が空だった場合はエラー表示される様に実装する事。
モデルにバリデーションを設定
バリデーションを付けたいモデル(今回はpost.rb)に記載。
post.rbclass Post < ApplicationRecord validates :title, :content, presence: true endtitleカラムとcontentカラムが空を防ぐ。
エラーメッセージのファイルの作成
エラーメッセージはエラーが発生すると、_error_messages.html.erb内に格納される。
layouts/_error_messages.html.erb% if model.errors.any? %> <div class="alert alert-warning"> <ul> <% model.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %>フォーム(form_with)の中にrenderで挿入
posts/new.html.erb<%= form_with model: @post, class: :form, local: true do |form| %> #renderメソッドで_error_messages.html.erbを呼び出す。 <%= render 'layouts/error_messages', model: form.object %> <%= form.text_field :title, placeholder: :タイトル, class: :form__title %> <%= form.text_area :content, placeholder: :ブログ本文, class: :form__text %> <%= form.submit '投稿する', class: :form__btn %> <% end %>これで表示は完了。
次は日本語化実装へ。エラーメッセージの日本語化
Gemfilegem 'rails-i18n'Gemfileに以下の一文を追加して、bundle install。
日本語化の基となるファイルを作成する
Railsの多言語化対応は、ymlファイルで管理。
config/locales ディレクトリ直下に、ja.ymlを作成。ターミナル$ touch config/locales/ja.ymlカラム名の日本語化
config/locales/models/ja.ymlja: activerecord: attributes: post: title: 名前 content: ブログ本文ja.ymlの注意点
Railsはymlファイルの改行とインデントで日本語化のパスを参照している為、
attributes: => モデル名 => カラム名 の順に改行とインデントを入れる必要がある。config/locales/models/ja.ymlattributes: # attributes:の直下に post: # モデル名を指定し title: 名前 # カラム名を指定する。 content:ブログ本文 # カラム名を指定する。
- 投稿日:2020-01-14T17:41:04+09:00
RSpecでActiveRecordに依存しているConcernのspecを書く。
例えばこのようなActiveRecordのschemaに依存しているConcernがある。
app/models/concerns/printable.rbmodule Printable extend ActiveSupport::Concern def print_id puts self.id end def print_name puts self.name end endそのような場合、どのようにspecを書くのが良いでしょうか。
何も考えずにやるなら既存のActiveRecordのModelを利用することでしょうか。app/models/user.rbclass User < ApplicationRecord include Printable endspec/models/user_spec.rbdescribe User, type: :model do before do record.save end describe '#print_id' do let(:record) { User.new } subject { record.print_id } it do expect { subject }.to output("#{nil}\n").to_stdout end end describe '#print_name' do let(:record) { User.new(name: 'hoge') } subject { record.print_name } it do expect { subject }.to output("hoge\n").to_stdout end end endが、これは
PrintableModuleのspecを書きたいのにUserModelに依存しており、責務が分けられておらず良くないです。
なので、RSpec上で仮のModelを作ってPrintableModuleだけをテストします。spec/models/concerns/printable.rbdescribe Printable, type: :model do before(:all) do m = ActiveRecord::Migration.new m.verbose = false m.create_table :pritable_tests do |t| t.string :name end end after(:all) do m = ActiveRecord::Migration.new m.verbose = false m.drop_table :pritable_tests end class PrintableTest < ApplicationRecord include Printable end before do record.save end describe '#print_id' do let(:record) { PritableTest.new } subject { record.print_id } it do expect { subject }.to output("#{nil}\n").to_stdout end end describe '#print_name' do let(:record) { PritableTest.new(name: 'hoge') } subject { record.print_name } it do expect { subject }.to output("hoge\n").to_stdout end end end更にリファクタリングをしてみます。
spec/spec_helper.rb# ... # ... # ... def create_spec_table(name, &block) before(:all) do m = ActiveRecord::Migration.new m.verbose = false m.create_table name, &block end after(:all) do m = ActiveRecord::Migration.new m.verbose = false m.drop_table name end endspec/models/concerns/printable.rbdescribe Printable, type: :model do create_spec_table :printable_tests, do |t| t.string :name end class PrintableTest < ApplicationRecord include Printable end before do record.save end describe '#print_id' do let(:record) { PritableTest.new } subject { record.print_id } it do expect { subject }.to output("#{nil}\n").to_stdout end end describe '#print_name' do let(:record) { PritableTest.new(name: 'hoge') } subject { record.print_name } it do expect { subject }.to output("hoge\n").to_stdout end end endこれで綺麗に書くことが出来ました。
- 投稿日:2020-01-14T13:00:04+09:00
devise のコントローラーに追加したインスタンス変数が view で参照できない件の対応メモ
発生した問題
Devise gem が自動生成するアカウント登録画面をカスタマイズしようとする
$ rails generate devise:controllers usersとりあえずコントローラーにインスタンス変数を定義してみる
app/controllers/users/sessions_controller.rbclass Users::SessionsController < Devise::SessionsController # GET /resource/sign_in def new super @foo = "This is a foo." end ... endなぜか view で参照できない
app/views/devise/sessions/new.html.erb<%# 何も表示されない %> <p> FOO: <%= @foo %> </p>解決方法
Devise のコントローラーをオーバーライドしたい場合は super にブロックを渡すのが正規のやり方だった。
# GET /resource/sign_in def new super do |resource| @foo = "aaa" end endちなみにブロック変数 resource には新規作成する対象のモデルのインスタンスが設定されている。
原因など
継承元の Devise::SessionsController の定義がこう。
def new self.resource = resource_class.new(sign_in_params) clean_up_passwords(resource) yield resource if block_given? respond_with(resource, serialize_options(resource)) end最後の respond_with は responders という別の gem のメソッドで、ここで view のレンダリングを行っている。
なので、super の後にインスタンス変数を設定しても、既に view の描画は終わっているため、view に反映されない。
--
yield if block_given?でコードの差し込みポイント作るのたまにやるけど、継承でも使えるのは盲点だったなぁ。
割と使えそうなテクニックなので覚えておこう。
参考資料
継承元(Devise::SessionsController)のソースコード
https://github.com/heartcombo/devise/blob/master/app/controllers/devise/sessions_controller.rbConfiguring controllers | heartcombo/devise - github
https://github.com/heartcombo/devise#configuring-controllers
- 投稿日:2020-01-14T11:22:55+09:00
【Ruby on Rails】デフォルトメッセージの日本語化
エラーメッセージなどのデフォルトメッセージを日本語化する設定です。
バージョン情報
Ruby 2.6.3
Ruby on Rails 5.2.3設定方法
config/initializers/配下にlocale.rbというファイルを作成します。ファイル名は何でも良いですが習慣上locale.rbにしてあります。下記の2文を記載します。
config/initializers/locale.rbI18n.config.available_locales = :ja I18n.default_locale = :ja日本語の辞書ファイルを作成します。
config/locales/にja.ymlというファイルを作成し、下記のURLの本文を作成したファイルにすべてコピペします。https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml
モデル名などはまだ英語のままなので、個別で日本語化していきます。
config/locales/にmodel.ja.ymlというファイルを作成します。config/locales/model.ja.ymlja: activerecord: models: post: 投稿 user: ユーザー attributes: post: content: 内容 image: 画像 user: name: 名前 email: メールアドレス current_password: 現在のパスワード password: パスワード password_confirmation: 確認用パスワード以上です。
- 投稿日:2020-01-14T11:02:01+09:00
#Rails ActionMailer + #Rspec でメール送信数 / 送信先をテストする例
subject { something } it do expect { subject }.to change { ActionMailer::Base.deliveries.count }.by(3) endbefore { subject } it do expect(ActionMailer::Base.deliveries.map(&:to)).to include ['alice@example.com'] expect(ActionMailer::Base.deliveries.map(&:to)).to include ['bob@example.com', 'carol@example.com'] endOriginal by Github issue
- 投稿日:2020-01-14T07:40:54+09:00
ページネーションと並び替えに対応した投稿一覧画面とAPIの実装【初学者のReact×Railsアプリ開発 第10回】
やったこと
- Reactでの投稿一覧画面の実装と並び替えに対応するためのRails APIの実装
- ラジオボタンの変更によるAPIからの投稿の取得と表示
- reduxを使った表示する投稿の状態管理
- material-ui-flat-paginationを用いたページネーションの実装
成果物
Rails APIの実装手順
route.rb: ルートの編集
route.rbRails.application.routes.draw do namespace :api, defaults: { format: :json } do namespace :v1 do get 'posts', to: 'posts#index' get 'posts_suki', to: 'posts#suki_index' get 'posts_allcount', to: 'posts#all_count_index' end end endposts_controller
- API側のページネーションの実装として、kaminariを用いています。
- ポストは1ページあたり10個ずつ返すようにしています。新着順や投票数順など、order('...')で、postsテーブルのどのカラムで並び替えするかを記述しています。
- page_lengthは、React側でページ数を何ページまで表示するかを確定させるために必要な情報です。46個の投稿なら5ページまでなど...
posts_controller.rbdef index posts = Post.page(params[:page] ||= 1).per(10).order('created_at DESC') page_length = Post.page(1).per(10).total_pages json_data = { 'posts': posts, 'page_length': page_length, } render json: { status: 'SUCCESS', message: 'Loaded posts', data: json_data} end def suki_index posts = Post.page(params[:page] ||= 1).per(10).order('suki_count DESC') page_length = Post.page(1).per(10).total_pages json_data = { 'posts': posts, 'page_length': page_length, } render json: { status: 'SUCCESS', message: 'Loaded posts', data: json_data} end def all_count_index posts = Post.page(params[:page] ||= 1).per(10).order('all_count DESC') page_length = Post.page(1).per(10).total_pages json_data = { 'posts': posts, 'page_length': page_length, } render json: { status: 'SUCCESS', message: 'Loaded posts', data: json_data} endReact実装手順
App.js
- ルートの編集です。
App.jsimport PostsList from './containers/PostsList'; <Auth> <Switch> <Route exact path="/" component={Home} /> <Route path='/create' component={Create} /> <Route path='/postslist' component={PostsList} /> </Switch> </Auth>PostsList.js
- ここでは一部のコードのみ紹介します。
- 表示させるポストなどの情報は、Redux(PostListReducer)で管理しています。
- componentdidmountで、初期描画の際に表示させるポストの取得を行っています。前回描画時の情報を保存しておくためにreduxでの状態管理を行っています。
- handleChangeらラジオボタンの変更に対応しています。
- handlePaginationClickは、ページリンクの変更に対応しています。offsetは1ページ目なら0、2ページ目をクリックしたときは10、3ページ目なら20...です。この情報で、APIで何ページ目の情報をもらうか確定させています。
Postslist.js//module import, css部分は省略 class PostsList extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.handlePaginationClick = this.handlePaginationClick.bind(this); } componentDidMount() { const { PostsListReducer } = this.props; if (PostsListReducer.selected === "新着順") { this.props.actions.getPostsList("", PostsListReducer.offset, "新着順") } else if (PostsListReducer.selected === "スキが多い順") { this.props.actions.getPostsList("_suki", PostsListReducer.offset, "スキが多い順") } else if (PostsListReducer.selected === "投票数が多い順") { this.props.actions.getPostsList("_allcount", PostsListReducer.offset, "投票数が多い順") } } handleChange(e) { if (e.target.value === "新着順") { this.props.actions.getPostsList("", 0, "新着順") } else if (e.target.value === "スキが多い順") { this.props.actions.getPostsList("_suki", 0, "スキが多い順") } else if (e.target.value === "投票数が多い順") { this.props.actions.getPostsList("_allcount", 0, "投票数が多い順") } } handlePaginationClick(offset) { const { PostsListReducer } = this.props; if (PostsListReducer.selected === "新着順") { this.props.actions.getPostsList("", offset, "新着順") } else if (PostsListReducer.selected === "スキが多い順") { this.props.actions.getPostsList("_suki", offset, "スキが多い順") } else if (PostsListReducer.selected === "投票数が多い順") { this.props.actions.getPostsList("_allcount", offset, "投票数が多い順") } }PostsList.js(render)
- 続いて、レンダーの部分です。
- PostListReducerの情報を使って、表示を制御しています。
- 各ポストには、リンク("/posts/post.id")を貼って、詳細ページに飛べるようにしています。
- RadioGroupタグと、Paginationタグの設定が多少頭を使います。
PostsList.jsrender() { const { CurrentUserReducer } = this.props; const { PostsListReducer } = this.props; const { classes } = this.props; return ( <Scrollbars> <div className={classes.container}> <FormControl component="fieldset"> <FormLabel component="legend"></FormLabel> <RadioGroup aria-label="position" name="position" value={PostsListReducer.selected} onChange={this.handleChange} row> <FormControlLabel value="新着順" control={<Radio color="primary" />} label="新着順" labelPlacement="end" /> <FormControlLabel value="スキが多い順" control={<Radio color="primary" />} label="スキが多い順" labelPlacement="end" /> <FormControlLabel value="投票数が多い順" control={<Radio color="primary" />} label="投票数が多い順" labelPlacement="end" /> </RadioGroup> </FormControl> <ul className={classes.ul}> {PostsListReducer.items.map((post) => ( <Link to={"/posts/" + post.id} className={classes.link}> <li className={classes.li} key={post.id}> <div className={classes.licontent}> <h3 className={classes.lih3}>{post.content}</h3> </div> </li> </Link> ))} </ul> <MuiThemeProvider theme={pagitheme}> <CssBaseline /> <Pagination limit={10} offset={PostsListReducer.offset} total={PostsListReducer.page_length * 10} onClick={(e, offset) => this.handlePaginationClick(offset)} /> </MuiThemeProvider> </div> </Scrollbars> ) } }actions/index.js
- ここでは、APIから投稿を取得しています。
- PostsListReducer.jsでstateを変更するためのactionの内容の記述とdispatchをしています。
index.jsexport const getPostsList = (fetchlink, offset, selected) => { return (dispatch) => { dispatch(getPostsListRequest()) const auth_token = localStorage.auth_token const client_id = localStorage.client_id const uid = localStorage.uid const page_url = offset / 10 + 1 return axios.get(process.env.REACT_APP_API_URL + `/api/v1/posts${fetchlink}?page=${page_url}`, { headers: { 'access-token': auth_token, 'client': client_id, 'uid': uid } }) .then(response => dispatch(getPostsListSuccess(response.data.data.posts, offset, response.data.data.page_length, selected))) .catch(error => dispatch(getPostsListFailure(error, offset, selected))) }; }; export const getPostsListRequest = () => ({ type: 'GET_POSTSLIST_REQUEST', }) export const getPostsListSuccess = (json, offset, page_length, selected) => ({ type: 'GET_POSTSLIST_SUCCESS', items: json, offset: offset, page_length: page_length, selected: selected, }) export const getPostsListFailure = (error, offset, selected) => ({ type: 'GET_POSTSLIST_FAILURE', items: error, offset: offset, selected: selected, })reducers/PostListReducer.js
- ここでreduxのstateの変更を行っています。
- initialStateに記述の通り、初期状態では新着順の1ページ目が表示されるようになっています。
PostListReducer.jsconst initialState = { isFetching: false, items: [], offset: 0, page_length: 1, selected: "新着順", }; const PostsListReducer = (state = initialState, action) => { switch (action.type) { case 'GET_POSTSLIST_REQUEST': return { ...state, isFetching: true, items: [], offset: "", page_length: "", }; case 'GET_POSTSLIST_SUCCESS': return { ...state, isFetching: false, items: action.items, offset: action.offset, page_length: action.page_length, selected: action.selected, }; case 'GET_POSTSLIST_FAILURE': return { ...state, isFetching: false, error: action.error, selected: action.selected, offset: action.offset, }; default: return state; } }; export default PostsListReducer;reducers/rootReducer.js
- rootReducerにPostsListReducerを追加しています。
rootReducer.jsimport { combineReducers } from 'redux' import { reducer as formReducer } from 'redux-form' import { routerReducer } from 'react-router-redux' import CurrentUserReducer from './CurrentUserReducer' import PostsListReducer from './PostsListReducer' const rootReducer = combineReducers({ CurrentUserReducer, form: formReducer, router: routerReducer, PostsListReducer }) export default rootReducer
- 投稿日:2020-01-14T03:04:21+09:00
ActionView::MissingTemplate in Groups#newの一例
1.どんなエラー?
renderメソッドで表示しようとしている、viewファイルがありませんという内容です
筆者の場合は同じgroupsフォルダ内のformというファイルで記載したものを引用しnewファイルで表示しようとしていました。(下記参照)
<エラー文>
<エラーに関係したgroupsフォルダ内のファイル>
new.html.haml.chat-group-form %h1 新規チャットグループ = render partial: 'form', locals: { group: @group }form.html.haml= form_for group do |f| .chat-group-form__errors %h2 10件のエラーが発生しました %ul %li nameを入力してください .chat-group-form__field .chat-group-form__field--left = f.label :name, class: 'chat-group-form__label' .chat-group-form__field--right = f.text_field :name, class: 'chat__group_name chat-group-form__input', placeholder: 'グループ名を入力してください' .chat-group-form__field.clearfix / この部分はインクリメンタルサーチ(ユーザー追加の非同期化のときに使用します .chat-group-form__field.clearfix .chat-group-form__field--left %label.chat-group-form__label{:for => "chat_group_チャットメンバー"} チャットメンバー .chat-group-form__field--right / グループ作成機能の追加時はここにcollection_check_boxesの記述を入れてください = f.collection_check_boxes :user_ids, User.all, :id, :name / この部分はインクリメンタルサーチ(ユーザー追加の非同期化のときに使用します .chat-group-form__field.clearfix .chat-group-form__field--left .chat-group-form__field--right = f.submit class: 'chat-group-form__action-btn'2.原因
構文を確認してみるとどこにも間違いがないため、悩んでいたところ、ありました、partial: 'form'という一文が。
partialというのはフォルダ内の"部品名となっているファイル"を引用しますよという意味があり、通常のファイルに対してpartialという縛りを増やすと読み込みを行いません。
つまるところpartialで参照したファイルのファイル名は"部品"であることを意味する'_(アンダーバー)'から始めなければならないのです。3.解決方法
結論としてはpartialを使って呼び出すファイルは"_(アンダーバー)"から始まるファイル名とすればエラー要因の一つが取り除かれるということになります。
ちなみに筆者の場合の例を確認してみましょう。上記に添付した筆者のファイル名を見てみるとformのファイル名が部品の形となっていませんね。なのでform.html.haml→_form.html.hamlとすることによりエラー文は解消されます。
- 投稿日:2020-01-14T00:09:23+09:00
Bugsnagで特定のエラーの通知を無効化する
最近の勉強で学んだ事を、ノート代わりにまとめていきます。
主に自分の学習の流れを振り返りで残す形なので色々、省いてます。
Webエンジニアの諸先輩方からアドバイスやご指摘を頂けたらありがたいです!BugsnagでRailsの不具合を通知
Railsのエラー監視にBugsnagを利用しています!Slackとの連携をしていてエラーが発生したら通知が飛ぶ様にしております。
今回は、Bugsnagのエラーで通知されるエラーは優先度が高いもののみにしたかったのサービス自体に影響がないエラーの通知を一旦無効化することになりました。
Bugsnagと黒魔術で、Railsの不具合調査を楽にする仕組み! | 株式会社スタメンBugsnagで特定のエラーを無効化する
Bugsnagの公式ドキュメントを確認してみると以下の様な記述がありました!
これによると個々のエラーは破棄できます。 破棄されると、エラーはダッシュボードからすぐに削除され、受信した後続のイベントは保存されなくなる様です!Discard individual errors # Individual errors can be discarded. When discarded, errors are immediately deleted from the dashboard and subsequent events that we receive are not stored.
Bugsnag docs › Product › Managing event usage特定のエラーを破棄する
今回、通知を無効化したいので特定のエラーを選択し画像の様に削除しました!
エラーを削除したら、このエラーの今後の発生は保存されず、イベント制限にカウントされません。とのメッセージが表示されました!
Discard specific errors via the additional actions (...) menu in the dashboard. Future occurrences of these errors won't be stored or count towards your event limit.この対応で無事、無効化できました!



















