データベースの修正・削除関連
【データベースの作り直し】
・データベースの名称の誤り
・エンコードなどのデータベース設定の誤り
上記のような場合はデータベースを作り直すとよい。
----------------------------------------------------------
【作り直しの方法①:データベース削除】
database.ymlに記載されているエンコード設定や
データベース名の誤りの際に行う。
データベース削除(rails db:dropコマンドを実行すると削除できる)
→database.ymlを正しい形に修正
→データベースの再作成(rails db:create)
という手順。
----------------------------------------------------------
【作り直しの方法②:データベース削除+マイグレーションファイルの適用】
データベース設定は誤っていないが、再度データベースを作り直して、
既存のマイグレーションファイルを適用させたい際に行う。
rails db:migrate:reset
このコマンドで、
データベースの削除→再度生成→既存のマイグレーションファイルを全て適用
が一括で行える。(ただし、データベースの保存内容は全て削除となる)
----------------------------------------------------------
カラムの設定ミスの修正をする際に行う。
この時、適用済みのマイグレーションファイルをそのまま修正してはいけない。
※rails db:migrate:statusでマイグレーションファイルの状態を確認できる
rails db:rollback
(最新のファイルだけDownになるので、必要な場合は複数回行う)
→マイグレーションファイル内のカラム設定の編集
→マイグレーションを実行してデータベースに適用
rails db:migrate
(ロールバックは1つずつしかできないが、rails db:migrateは全てのファイルをupにする)
----------------------------------------------------------
【作り直し④:テーブルの再作成】
→rails d modelでモデルを削除
→rails g modelでモデルを再作成
→マイグレーションファイルを編集してrails db:migrate
【マイグレーションファイルをupのまま削除してしまった場合】
テーブルを作り直すために、誤って手動でマイグレーションファイルを消したり、
upのままモデルをrails d modelで削除してしまったような場合が該当する。
rails db:migrate:statusで確認すると、
upのまま削除されたファイルがNO FILEとして残っている。
このファイルはそのままではロールバックできないので、
ファイルのMigration IDを利用して同じ名称で再度ファイルを作成する。
(コピーして手作業で作って、ファイル内にマイグレーションファイルの
デフォルト部分だけを記述する)
↓MigrationID_sample.rbという名称で作成した場合
class Sample < ActiveRecord::Migration[6.0] def change end end
この後、rails db:migrate:statusで状態を確認してロールバックを行い、
Downにしてから削除する。
最終課題で詰まったところ(更新中)
【GitHubでの流れ】
コミット→プッシュ→プルリクエスト→レビュー修正→LGTM→マージ
→不要ブランチの削除→ GitHub Desktopでブランチをマスターに→Fecth
【カラム名のミス】
「下の名前」のカラム名を「last_name」としたままマイグレーションを行なっていたので修正。
①rails generate migration 変更前カラム名_column_to_モデル名(複数形)
→変更用のマイグレーションファイルを作成
②作成したマイグレーションファイルを編集
今回は2つのカラム名を修正。下記を記述後にrails db:migrateで完了。
Squel Proでも反映を確認した。
【マイグレーションファイルのエラー】
・マイグレーションファイルで、追加したカラム名の後にコンマを書き忘れてエラー
・原因不明でマイグレーションファイルが複数になっていた
→モデルを一度削除して再度インストール(rails d devise user)
→この際、マイグレーションファイルの状態はrake db:migrate:statusで確認した
【ユーザー管理機能のパラメーター設定】
application controllerへ下記のように記述して設定。
if~で、devise controllerの処理時限定とする。
【erbファイルのデバッグ】
erbファイルへのbinding.pry実行でエラーが発生した。
原因:pry railsのインストールやサーバーの再起動を行わずにbinding.pryを実行していたためエラーが発生した。また、erbファイルの場合はbinding.pryの記述を<% %>で囲む必要がある。
手順:Gem fileへのgem 'pry rails'の記述を行いbundle installを実行
→その後rails sを行う
【binding.pryの終了の仕方】
binding.pryの終わらせ方が分からなかった(exitで終了できなかった)
→exit!で終了できた
【ビューファイルの設置ミス】
課題用に与えられていたビューファイル(部分テンプレート)を設置する場所を誤っていた。
正:views/sharedファイルを作成して設置
誤:元々あったviews/devise/sharedファイルに設置
deviseインストールからやり直した時に、deviseがデフォルトで作成してくれるサインアップ画面のビューファイルを確認。
エラーメッセージなどの部分テンプレートの引用先がdevise/sharedとなっていた。
対して課題用のビューファイルでは、部分テンプレートの引用先がsharedの記載だけだったので判明。
(そもそもヘッダーやフッターの部分テンプレートなので、全体に適用する意味でもdeviseディレクトリ内にならないことにも気づいた)
【エラー発生時にメッセージを表示する部分テンプレート】
上記のように記載があったので、hogeを@userに変えたがエラーが発生。
Googleで「render model」と検索するとサブジェクトでf.objectが出てきて、検索結果から下記サイトを確認。
Ruby - インスタンスではなくf.objectを使う理由を知りたい|teratail
model:の後はhogeを消してそのままf.objectで良かったらしい。
【ユーザー新規登録を行っても保存されない】
ユーザー新規登録を行っても、再度ユーザー登録画面に戻される
対処:
①メールアドレスを変えた(一意性が関係しているかもと思った)
→結論関係なかった(後から確認)
②ビューファイルのフォーム名をencrypted_passwordではなくpasswordに変更した
③deviseのストロングパラメーターはemail・password・password_confirmation・encrypted_passwordは入れずにその他のカラムを指定した
④戻されたユーザー登録画面ではなく、再度サインアップページのパスを入力してページを切り替えた(localhost3000/usersのページに飛ばされていたため)
あるサイトで下記の表記を見つける。
(ただし、このサイトには間違った情報も書いてあるっぽい)
deviseでユーザー登録ができない時に疑うところ6点 - Qiita
deviseでのusersテーブルのカラム(デフォルトではencryped_password)ですが、form_withのパスワードとパスワード再入力では、それぞれカラム名をpassword、password_confimationと記述しないといけません。これはpasswordとpassword_confimationが同じかどうかをチェックし、同じならばencryped_passwordに暗号化した文字列を返すという機能がdeviseに実装されているからです。
あとで確認すること:binding.pryを行った時にpermitted:がどうなっているか・メールアドレスは変える前のもので登録できるか?飛ばされたページでも登録できていたか
【ログアウト機能実装時のエラー】
発生したエラー:下記画像のエラー(Routing Error)
エラー時の状況:link_toでログアウトの機能を実装した際に発生
確認したこと:ルーティングの設定ミスがないか・実装漏れしている設定がないか
確認方法:過去のカリキュラム・エラー文でのGoogle検索(解決せず)
解決:過去作成したアプリ(Ptotospace)の同じ箇所を撮影して見比べ
原因:不要なダブルクォーテーションがあったこと。
(ルーティングは正しかった)
配布されていたビューファイルのパスの部分が"#"になっており、
#の部分だけを修正したことで発生したエラー。
【テストコード】
誤っていた内容:FactoryBotの導入時
(Gemfileのgroup :development, test doの中に記述する必要があった)
②FactoryBot用のファイルは、
FactoryBot導入後→テストファイル作成時に自動生成されるが、導入前に作成したテストファイルに対応するものは自動生成されないので、手動で作る必要がある。
【強制的にログインページに飛ぶ】
application_controller.rbに、カリキュラムで学んだauthenticate user!を記載していた。
これを記述すると、ログインしていないユーザーを強制的にログインページに誘導できる。
今回は未ログインでもトップページは利用できる実装のため、記述不要だった。
【エラーコードの重複】
エラーコードが重複で表示される現象が発生。
バリデーションにallow_blank: trueを追記して解消。
(詳しくはテストコードのブログにまとめ)
【パスワード(確認用)】
パスワード(確認用)は、データベースに保存されない仮想の属性のため、
バリデーションを設定する必要がない。
【Fakerの設定】
Fakerで「半角英数字でメールアドレスを作成」させる際、
password { Faker::Internet.password(min_length: 6) }
としていた。
しかしこの記述では、「数字のみ」や「英字のみ」のパスワードが生成されてしまい、テストが失敗することがある。
英数字混合のpasswordを想定した上でFakerを使用する場合は、
password { '1a' + Faker::Internet.password(min_length: 6) }
のように、確実にpasswordに英数字が含まれるように指定しなければならない。
(またFakerを用いず、直接値を書き込む形でも問題ない)
【半角英数字に対するテスト】
「半角英数字での入力が必須」というテストコードを記述したが、正確性向上の為、「半角である必要がある」と「英数字である必要がある」は分けてテストした方が良い。
【バリデーションの可読性向上】
バリデーションの記述はwith_optionsを用いることでまとめられる。
可読性向上のために修正を行なった。
validates :nickname, presence: true
validates :family_name, presence: true
validates :family_name, format: { with: /\A[ぁ-んァ-ヶ一-龥々ー]+\z/ }, allow_blank: true
validates :first_name, presence: true
validates :first_name, format: { with: /\A[ぁ-んァ-ヶ一-龥々ー]+\z/ }, allow_blank: true
validates :family_name_furigana, presence: true
validates :family_name_furigana, format: { with: /\A[ァ-ヶー-]+\z/ }, allow_blank: true
validates :first_name_furigana, presence: true
validates :first_name_furigana, format: { with: /\A[ァ-ヶー-]+\z/ }, allow_blank: true
validates :birth_day, presence: true
上記を↓のように書き換えた。
【メールアドレスの一意性】
メールアドレスの一意性は、devise作成時にデフォルトで設定されているようで
特にマイグレーションファイルへの記述の必要はない。
↓マイグレーションファイルの下の方に書いてある。
【fill_inを使う際のフォーム名】
分かっていなかった点:fill_inを使う際に指定するフォーム名をどこから参照するか
確認方法:検証ツール→左上のカーソルマークのボタンを押す→そのフォームのラベル名(フォームのすぐ上にある表題のような部分)にカーソルを合わせる→<label></label>で囲まれた部分がフォーム名になる。
ただし、label内にfor='id名'が記載されている必要がある。このforを記載することでlabelとformを紐づけるため。
【resourcesの使い方】
resourcesを記述する場所と意味が分かっていなかった
記述する場所:routes.rb(controllerに記述していた)
意味:CRUDで用いられる7つのアクションをまとめて設定するためのもの
※CRUDはアプリケーションでデータの取り扱いを行う上での基本的な処理
(Create・Read・Update・Deleteの頭文字)
また、resouces onlyの設定をするときに[]で囲んでいなかったことで、
rails routesを行ったときにエラーが発生した。
【バリデーション】
文字数の指定・数値の範囲の指定について、下記サイトを参考にした。
Rails 6.1: 数値バリデーションを範囲(..)で指定できるinオプションが追加(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社
↓maximumとnumericalityを用いて記述。
【プルダウンの設定時】
プルダウン用のモデルを作成し、項目を記述して読み込みをしたがエラー発生。
理由:各プルダウンの行ごとにカンマで区切るのを忘れていた。
↓下記はきちんと区切れた記述。
※上記実装中、特定のプルダウンだけエラーになる現象が発生。
下記は修正後で、Charge.all(モデル名:charge)としているが、元々はDelivery_charge.all(モデル名:Delivery_charge)だった。
エラー内容は、Delivery_charge.alllが定義されていないという内容。
他のとの相違点は、モデル名の中にアンダースコアを入れていたこと。
ここが怪しいと思い、アンダースコアのないchargeモデルにして試してみたところ解消。
【ログアウト状態だとトップ画面に行けない】
authenticate_user!
→ログアウト状態のユーザーに対して、ユーザーログイン画面に遷移させる機能。
ただ、ログアウト状態でも使えるページを実装する場合には、
下記のように対象ページをexceptで除外する必要がある。
また、最初はこれをapplication_controller.rbに記述していたが、
products_controllerだけに機能させないといけないので
記述場所を変更した。(レビューによる修正)
【エラーメッセージが出ない】
商品出品機能実装時、未入力の状態で登録をしてもエラーメッセージが出なかった。
エラーを表示させる書き方自体は(元々テンプレートで配信されたものなので当然)正しく、
コントローラーのcreateメソッドに条件文で「保存できなかった時」にnew画面に戻る記述をすると解消した。
【パラメーターがうまく機能しない】
パラメーターの記述をした後に商品登録をしようとした際に、パラメーターがpermitされなかった。
userとアソシエーションを組んでいるにも関わらず、ストロングパラメーターにおいてユーザーをマージしていなかったことが原因だった。
【JavaScriptの実装】
価格のフォームに金額を入力すると、
販売手数料や販売利益が自動的に反映する実装。
↓application.jsにrequire("../product_price)の記載
..は1つ上のディレクトリ、product_priceはJavaScriptを記述するために手動で作成したファイル
↓手動で作成したファイルにJavaScriptのコードを記述。
1行書き込むごとに、console.log("OK")などとして動作を確認した。
(binding.pryのようなイメージ)
html.erbファイルで、application.jsを読み込むためのscriptタグの挿入を忘れていると正常に反映されないので注意する。
【レビュアーからの修正指示(ER図)】
・電話番号はstring型にする
(integer型だと0落ちという、頭の0が消える現象が起こる可能性があるため)
・テーブル名から推測できる部分は名称から省略する
(productsテーブルならproduct_nameではなくnameにするなど)
・password_confirmationは不要
(deviseのgemがあらかじめ用意している`encrypted_password`を使用しているため。
また、パスワードはセキュリティの面からも直接データベースには保存を行わず、
encrypted_passwordがユーザーの入力したパスワードを自動的に暗号化してくれている)
・referenceではなくrefirencesとする
(マイグレーション実行時にエラーとなるため)
・ActiveHashで実装する項目は、カラム名に_idをつける。
(各プルダウン項目ごとに番号を割り振って、それをデータベースに保存するため)
・誕生日のカラムはDate型にする。
(プルダウンが西暦・月・日で分かれていたので3つのカラムにしたが、1つのDateで実装可能)
・emailは一意性なので、ER図のオプションに記載する。
(ただし、機能自体はdeviseのデフォルトで備わっているので追加する必要はなく、バリデーションへの記載も不要)
【レビュアーからの修正指示(ユーザー管理機能)】
・password_confirmationにはバリデーションは設定しない。
(仮想の属性でカラムに保存しないため。同様にpasswordも保存しない。代わりに暗号化されたencrypted_passwordを保存する)
・Fakerへの設定
password { Faker::Internet.password(min_length: 6) }
この記述では、稀に「数字のみ」や「英字のみ」のパスワードが生成されてしまい、テストが失敗することが考えられる。
英数字混合のpasswordを想定した上でFakerを使用する場合は、
password { '1a' + Faker::Internet.password(min_length: 6) }
のように、確実にpasswordに英数字が含まれるように指定する。
(Fakerを用いず、直接値を書き込む形でも問題ない)
・「passwordは半角英数混合でなければ登録できない」のテストは下記の2つに分ける。(テストの正確性を向上させるため)
passwordが半角英字のみの場合は登録できない
passwordが全角の場合は登録できない
・バリデーションは「with_options」を使用することで、共通したオプションを付けることが可能となり、コードの可読性が上がるのでまとめる。
【レビュアーからの修正指示(商品出品機能)】
・active_hashで実装した項目に関しては、未選択状態の項目である「---」では保存できないことを確認する必要がある。
値に1を代入し、2以上のidが選択されないと保存できないことを確認するテストコードを実装しなければならない
・商品価格は「英数字混合では保存できないこと」のテストも必要。
・link_toの遷移先はPrefixで記述する。
【ActiveHashのプルダウン項目を別のビューに反映させる】
ActiveHashのプルダウン項目はidで保存されるので、そのまま引用してビューに反映させると単に数字として表示される。
これをプルダウン項目の表示のまま反映させることができなかった。
結論、下記のようにクラス変数にidを添字として渡すだけだった。
(delivery_charge_idはプルダウンの番号、Charge.dataは下に画像添付しているプルダウン用のモデルのクラス変数)
添字を渡すことには早めに気づいたのだが、最初は単にid = 添字としていたので、ズレが生じていたことからうまくいかなかった。
id - 1を添字として渡すことで解決した。
試したこと(binding.pryで処理を止めてコンソールに色々出力させた)
・Charge.dataと入力→配列が全て出てくる
・delivery_charge_idと入力→3が出てくる
・Charge.data[3]と入力→3つ目のハッシュが出てくる
・Charge.data[3][:name]と入力→送料込み(出品者負担)が出てくる
・Charge.data[delivery_charge_id][:name]と入力→エラー
・Charge.data[delivery_charge_id - 1 ][:name]と入力→送料込み(出品者負担)が出てくる
最後の添字に-1をするまでが長くかかった。また、delivery_charge_idは#{}で囲むのかなど、きちんと理解できていなかった点も確認をした。
ActiveHashについて
最終課題実装中に学んだ内容。
【ActiveHash】
都道府県のように、基本的に変わらないデータを取扱う際に用いる。
・データが変わらないのでDBに保存する必要はない
・一方で、ビューファイルに記述するには可読性に欠ける
ようなときに使うと良い。
ActiveHashはGemの1つで、上記のデータをモデルファイルに記述する。
この記述に対しては、ActiveRecordメソッド(allやcreateなど)を使うことができる。
【使い方】
Gemfileにgem 'active_hash'を追記して、bundle installを実行。
対象のモデルに対して、設定したいデータを下記のように記述。
1行目の記載は、ActiveHash::BaseをGenreモデルで継承するという意味。
あるモデルにActiveHashを記述するとき、この継承が必要となる。
また、継承することでActiveHash内でActiveRecordメソッドが使えるようになる。
(ActiveHash::BaseはActiveHashのGem内で定義されている)
【アソシエーションの設定】
ActiveHashを用いてアソシエーションを設定する場合には、モジュール(module)を活用した特殊な記述が必要となる。
モジュールはActiveHashで定義されており、それをモデルに取り込む形をとる。
※モジュールとはメソッドをまとめているもので、取り込んで使用するという程度の理解で今は良いらしい。
↓今回はarticleモデルとgenreモデル間でのアソシエーション。
↓genreモデル側の記載は下記。
【numericality】
バリデーションの1つで、数値かどうかを検証する。
今回は、プルダウンの「--」が選択されている場合はデータベースに保存させないようにするために、下記のようにした。
(genre_id1が「--」なので、それを除くという記述)
【コントローラー作成時のビューファイル同時作成】
コントローラー作成時にビューファイルも同時に作成できるが、
下記のように2つ指定することもできる。
【プルダウンの作成】
プルダウンの作成は、6行目のf.collection_selectを用いる。
<%= form.collection_select(保存されるカラム名, オブジェクトの配列, カラムに保存される項目, 選択肢に表示されるカラム名, オプション, htmlオプション) %>
・第一引数
genre_id→モデルで記述したActiveHashのidの値が、カラムのどこに入るかを指定した記述(例えばITを選択すると、genre_idに6という値が保存される)。
・第二引数
Genre.all→プルダウンリストを参照する記述。Genreモデルに記載されているActiveHashが、ActiveRecordのallによって全て参照される。
・第三引数
:id→カラムに保存される項目(今回であれば6になり、モデルでITが6であると指定している)
第四引数
:name→実際にプルダウンに表示されるもの(今回はジャンル名)
第五引数
{}→今回は中が空だが、必要な場合にオプションを記述することができる。
第六引数
{class:"genre-select"}→htmlオプションを記述することができる。
(今回はCSSを適用させるためにクラス名を付与している)
↓これで下記のようにプルダウンが作成できる。
【マイグレーションファイルを伴わないモデルの作成】
最終課題では、下記サイトを参考にActiveHash用にモデルのみの作成をした。
rails g model category --skip-migration
テストコードについて(まとめ)
TECH CAMPカリキュラムで学習したテストコードについて、
詳細を自分用にまとめ。
特に単体テストコードについてはかなり詳しくアウトプットしたので、
これを見ればテストコードの基礎は一通り把握できるはず。
【テストコード】
アプリケーションがきちんと動作するかどうかを、手動ではなくコードで確認する方法。
・ミスや抜け漏れ防止
・負荷を低減
・アプリケーションの仕組みを把握できる
といったメリットがある。
Ruby on Railsでは、RSpec(アールスペック)というGemを使う事で
便利に実装出来る。
----------------------------------------------------------------
【テストコードの種類】
正常系:ユーザーが開発者の意図通りに操作を行った際の挙動確認
異常系:意図と異なる操作を行った場合の挙動確認
の主に2種類がある。
また、それぞれに
単体テストコード:コントローラーやモデルなどの機能ごとに問題がないか確認
結合テストコード:ユーザーが行う一連の流れを確認
がある。
----------------------------------------------------------------
【テストコード作成の流れ】
RSpecのGemを追加(Gem Fileのdevelopment testに記述)
→bundle install
→rails g rspec:install(アプリケーションにRSpecをインストール)
→テストコード結果をターミナルで可視化する為にrspecファイルを編集(.rspecに--format documentationと追記)
→rails g rspec:model user(Userモデルのテストファイル作成)
----------------------------------------------------------------
【describe】
テストコードのグループ分けをするメソッド。
どの機能に対してテストを行うかを区分けする。
【it】
同様にグループ分けをするメソッドで、どのような状況のテストを行うか、
より詳細に内容を明記する(discribeメソッドの中で記述する)。
※itで分けたグループをexampleと呼ぶ
----------------------------------------------------------------
【テストコードの実行】
テストコードの実行は、ターミナルでbundle exec rspecコマンドを使用する。
bundle exec rspec ディレクトリ/ファイル名
※itの中が空でもコマンド実行できる。結果は下記のように表示される。
----------------------------------------------------------------
【valid?】
バリデーション実行の結果、下記のように処理をするメソッド。
・エラーがない場合はtrueを返す
・エラーがある場合はfalseを返し、エラーメッセージを生成する。
バリデーションは本来データベース保存の前にしか実行されないが、
このメソッドがあれば任意のタイミングで実行させられる。
----------------------------------------------------------------
【expectation】
想定通りの挙動をするかを検証する構文で、結果をtrue/falseで返す。
(expect = 期待する)
ひな形はexpect().to matcher()
テストの内容によって、引数やmatcerを変更する。
expectの中には、検証で得られる挙動(または確認したい事項)が入る。
----------------------------------------------------------------
【matcher】
どのような挙動(結果)を想定しているかを記述する部分。
つまり、expect内に記述する内容はこうなるであろうという予測を書くものがmatcherである。
また、matcherには様々な種類がある。
代表的なマッチャは
・include→include(引数)とし、expect内にその引数があるかを確認する。
・eq→eq(引数)とし、expectの結果がその引数かどうかを確認する。
expect(['りんご', 'バナナ', 'メロン']).to include('メロン')→true
expect(1 + 1 ).to eq(2)→true
----------------------------------------------------------------
【エラーメッセージの抽出】
errors→インスタンスにエラーがある場合にエラー内容を返すメソッド。
full_messages→エラー内容から、エラーメッセージを取り出す
下記は、rails cを用いてvalid?・errors・full_messagesの3つを整理したもの。
user.errorsでエラー内容は分かるが、
expect内に記述できないのでfull_messagesで取り出す。
https://gyazo.com/e2055fdb1eeea4c58a894764a4feef53
ちなみに、date型カラムの値を書くときには'2000/01/01'のような記述となる。
クォーテーションで囲まないと2000 ÷ 1 ÷ 1になるので注意。
(最初は2000-01-01と書いたので、birth_day:1998となった)
----------------------------------------------------------------
【FactoryBot】
インスタンスを別ファイルで書き留めておけるGem。
複数回使うようなインスタンスを別ファイルに記述しておき、buildを行う事で簡単に引用作成出来る。
各テストコードごとにbuildするのではなく、beforeメソッドを使って事前にbuildしてインスタンス変数にしておく事も可能。
・FactoryBotの導入
Gemfileのgroup :development, test doの中にgem 'factory_bot_rails'の記述
→bundle install
→FactoryBot導入前にテストファイルを作成していた場合は、対応するFactoryBot用のファイルを手動で作成する(導入後にテストファイルを作成すると、対応ファイルは自動生成される)
↓手動で作成したファイル
・FactoryBotの設定方法
↓users.rbの中に下記のように記述すると、FactoryBotのベースが設定できる。
・設定後の使い方
テストコード用のファイルで、buildメソッドでベースのFactoryBotを呼び出す。
呼び出した時点ではベース設定のまま全ての項目が埋まっているので、テストしたい項目を次の行で任意に指定(今回はブランクに上書き)する。
----------------------------------------------------------------
【before】
上記では各exampleごとにFactoryBotを作成していて記述が重なっている
→beforeを用いて、1回の記述で済むようにすると効率的。
この際、beforeから変数を渡す場合、インスタンス変数にしなければならない。
また、beforeは、テストコードのグループ分けをする(つまりdescribeの前)に記述する。
----------------------------------------------------------------
【Faker】
設定する事で、Fakerメソッド内で用いた文字列や値をランダムなものにする事が出来るGem。
FactoryBotと組み合わせると、自動的に生成されるインスタンスの中身をランダムに出来る。
都度テスト用のユーザーネームやパスワードを打ち込まなくても自動で当て込んでくれるので効率的になる。
・Fakerの導入
Gemfileにgem 'faker'を記述してターミナルでbundle installを実行する。
※テストコードに用いるGemなので、FactoryBot同様にgroup :development, test doの中に記述する。
・Faker導入後の流れ
↓Fakerを用いて、下記のようにランダムな値を取得することができる。
これをFactoryBotのファイル内で同じように設定すればOK。
ちなみに、Fakerで誕生日をランダム生成する記述は下記から確認した。
その他の項目を生成する時も 、1つ前のdefaultのディレクトリから調べられるようである。
※後でレビューをもらって分かったこと
【Fakerの設定】
Fakerで「半角英数字でメールアドレスを作成」させる際、
password { Faker::Internet.password(min_length: 6) }
としていた。(上記の画像でもそうなっている)
しかしこの記述では、「数字のみ」や「英字のみ」のパスワードが生成されてしまい、テストが失敗することがある。
英数字混合のpasswordを想定した上でFakerを使用する場合は、
password { '1a' + Faker::Internet.password(min_length: 6) }
のように、確実にpasswordに英数字が含まれるように指定しなければならない。
(またFakerを用いず、直接値を書き込む形でも問題ない)
----------------------------------------------------------------
【deviseで自動設定されるバリデーション】
・validatable
メールアドレス→必須である・一意性(他と被ってはいけない)・@を含む必要がある
パスワード→必須である・6〜128文字であること
※下記はdevise導入時にデフォルトでバリデーションが備わっている
メールアドレスが必須であること
メールアドレスが一意性であること
メールアドレスは、@を含む必要があること
パスワードが必須であること
パスワードは、6文字以上での入力が必須であること
パスワードとパスワード(確認用)、値の一致が必須であること
----------------------------------------------------------------
【パスワードに半角英数字を必須とする実装】
deviseの基本機能には備わっていないようで、過去のカリキュラムを確認。
正規表現で実装できそうなことは分かったが、具体的な設定方法は確認できず。
Google検索で下記のURLを参考に、バリデーションに記述。
テストしたところ、英数字混合でない場合はパスワードが無効になった。
↓実際の記述。
formatは、withオプションで与えられた正規表現がマッチするかどうかを判定する。
↓テストコードの記述。
↓テスト実行時のターミナル。
※後でレビューをもらって分かったこと
【半角英数字に対するテスト】
「半角英数字での入力が必須」というテストコードを記述したが、正確性向上の為、「半角である必要がある」と「英数字である必要がある」は分けてテストした方が良い。
※後でレビューをもらって分かったこと
【パスワード(確認用)】
パスワード(確認用)は、データベースに保存されない仮想の属性のため、
バリデーションを設定する必要がない。
上記はpassword_confirmationのバリデーション削除前。
----------------------------------------------------------------
【全角かな/カナ/漢字】・【全角カナのみの場合】
同様の設定でOK。
正規表現の記述は下記のサイトが有用。
----------------------------------------------------------------
【FactoryBotに全角漢字・かな・カナを生成させる方法】
デフォルトでは備わっていない機能。
追加でGimeiというGemを導入して対処した。
↓Gemfileのdevelopment, :testの項目にgem 'gimei'を追記してbundle install
↓導入すると、下記のようにランダムな名前が作れるようになる。
https://gyazo.com/371e1e4b508f9cd467077dbeaaaf2c65
↓最後に、FactoryBotと組み合わせるためにfactoryのファイルを編集。
(transient do ~ end部分も忘れずに)
↓テストコードは下記のように記述する。
↓Gimeiは下記のサイトに詳細が説明されている。
----------------------------------------------------------------
【be_valid】
正常系テストで使うメソッド。
valid?の結果がtrueであることを期待するという意味。
(trueですよね?ということ)
「この○○はbe_validです」と記述し、エラーが出ないかをチェックする。
(○○は、例えば文字数制限内のメールアドレスやニックネームなどを入れる)
コードの書き方
expect(○○).to be_valid
→○○はvalid?がtrueですよね?という文章。
rails c→FactoryBotからuserをbuild→valid?でチェック→true
ということで、下記のテストがtrueになる。
----------------------------------------------------------------
【正常系と異常系の記述区分け】
正常系と異常系は、contextを用いて記述する場所を区分けする。
↓下記のように、describeの中にcontextの階層を作って分ける。
----------------------------------------------------------------
【整理】
正常系はexpectとbe_validを紐付けて、trueであることを確認する。
異常系はvalid?メソッドでエラー文を出力させて、エラー文をexpectと紐づける。
エラー文には様々な種類があり、binding.pryで途中で処理を止めて確認する手法をとる。
(ちなみに、binding.pryは複数記述するとその都度処理を止めることが可能)
----------------------------------------------------------------
【メールアドレスの一意性テスト】
メールアドレスが他ユーザーと重複する場合にエラーとなることについてのテスト。
この場合、FactoryBotで作成したユーザーの1人目を保存
→2人目のユーザーを作成し、1人目と同じメールアドレスを指定
→エラーが発生するがテスト
という流れとなる。
↓最初は@user.saveをしておらず、一意性エラーにならなかった。
(saveをしないとテーブルに保存されないので、重複にならない)
----------------------------------------------------------------
【異常系のテストコード】
バリデーションに引っかかるような動作をコードで記述
→発生するエラーコードの文章を予測して「このようなエラー文章になればOK」とあらかじめ作っておく
→エラーコードの文章を取得して、予測した文章とマッチしていればOK
=エラー時はきちんとバリデーションが働く
この際に活用するのがエクスペクテーションというテストコードで、expectメソッドとerrors_full_messagesメソッドを用いる。
----------------------------------------------------------------
【エラーコードの確認方法】
コンソールで○○.valid?
→falseと表示される
→○○.errors.full_messages
→エラー文が表示される
※○○にはインスタンスが入る
異常系テストで、エクスペクテーションを行うにあたりエラーコードを入力するが、その確認の為に上記方法を用いる。
nil→何も存在しない事(ユーザーやツイートが何もない時など)
' '→文字列が空の状態(テキストやメールアドレスが空の時など)
異常系テストでバリデーション確認をする際に、上記を使い分ける。
また、エクスペクテーションで使うincludeはsは付かないのでスペルに注意。
----------------------------------------------------------------
【エラーメッセージの重複】
例)名字が空欄の場合
→「空欄不可」「正規表現」の2つのバリデーションを設定していると、エラーメッセージも2つ表示される。
ビューにエラーメッセージを表示させる記述をしている場合、
2つのエラー文が出てきてしまう。
解消するためには、バリデーションへの追記が必要。
↓これを
↓こうする。
allow_blank :trueを記述することで、ブランクの場合はバリデーションを適用させないようにできる。
presence trueとformat~を同一の行に記述したままだと、presence trueにもallow~が適用されてしまうので注意。
↓下記のサイトを参考にした。
----------------------------------------------------------------
【バリデーションの可読性向上】
バリデーションの記述はwith_optionsを用いることでまとめられる。
可読性向上のために修正を行なった。
validates :nickname, presence: true
validates :family_name, presence: true
validates :family_name, format: { with: /\A[ぁ-んァ-ヶ一-龥々ー]+\z/ }, allow_blank: true
validates :first_name, presence: true
validates :first_name, format: { with: /\A[ぁ-んァ-ヶ一-龥々ー]+\z/ }, allow_blank: true
validates :family_name_furigana, presence: true
validates :family_name_furigana, format: { with: /\A[ァ-ヶー-]+\z/ }, allow_blank: true
validates :first_name_furigana, presence: true
validates :first_name_furigana, format: { with: /\A[ァ-ヶー-]+\z/ }, allow_blank: true
validates :birth_day, presence: true
上記を↓のように書き換えた。
with_optionsの中にwith_optionsを入れ子構造にすることもできる。
----------------------------------------------------------------
【コントローラーの単体テスト】
モデルの単体テストの場合は、インスタンスを生成して、モデルに規定した内容(バリデーションなど)がその通りに働いているかを確かめる。
対してコントローラーの単体テストは、あるアクションにリクエストを送ったとき、想定通りのレスポンスが生成されるかを確かめる
→このテストには、Request Spec(Rspecのうちの1つ)という手法を使う。
(RSpecが提供している、コントローラーのテストコードを書く為に特化した手法で、Rspecを導入していれば使える)
----------------------------------------------------------------
【createメソッド】
FactoryBotで生成した値をデータベースに保存する為に活用する。
テスト用のDBに値を一旦保存するが、1回のテストが終了するごとに
保存された値が全て消去となる。
(ActiveRecordのcreateメソッドとは異なる)
FactoryBot.create(:tweet)のように使う。
----------------------------------------------------------------
【get】
get ○○_pathのように使う事で、どこのパスにリクエストを送るかを決めることが出来る。
コントローラーの単体テストで、リクエスト送信部分として使用。
(対応パスはrails routesで確認する)
----------------------------------------------------------------
【response】
リクエストに対するレスポンスの事。
binding.pryで停止している時にコンソールで入力すると、
レスポンスとしてたくさんの情報が表示される。
またresponse.bodyとすると、ブラウザに表示されるHTMLの表示部分が取得できる。
----------------------------------------------------------------
【HTTPステータスコード】
HTTP通信の処理の結果を数字で示すもの。
100〜 処理継続中
200〜 処理成功
300〜 リダイレクト
400〜 クライアントエラー
500〜 サーバーエラー
response.status
→レスポンスの中からステータスコードを確認できる。
(binding.pryで停止時)
----------------------------------------------------------------
【System Spec】
結合テストコードを記述する為に活用する技術(仕組み)のこと。
大枠の記述はRSpecと変わらない。
----------------------------------------------------------------
【Capybara】
System Specを記述する為に必要なGem。
Gemfileに標準で記載され、導入済み。
----------------------------------------------------------------
【System Specの使い方】
rails g spec:system usersのコマンドでファイルを生成する。
(これにより、Specフォルダ内にSystemフォルダとusers.spec.rbファイルが作られる)
----------------------------------------------------------------
【ユーザー新規登録に関するexample(結合テストコード)】
大枠は下記の2つで、contextで分ける。
①ユーザー新規登録ができるとき
②ユーザー新規登録ができないとき
さらにそれぞれのケースをユーザー目線で細分化していく。
----------------------------------------------------------------
【visit】
visit 〇〇_pathで指定のページに遷移する。
----------------------------------------------------------------
【page】
visitで訪れたページの見える分だけの情報が格納されている。
(カーソルを合わせないと見れない文字列は含まれない)
----------------------------------------------------------------
【have_content】
expect(page).to have_content(‘〇〇’)で
訪れたページの中に〇〇という文字列があるかどうかを判断するマッチャ。
have_no_contentになると、逆に文字列が無いことを確かめるマッチャになる。
(異常系テスト時に使用)
----------------------------------------------------------------
【fill_in】
fill_in ‘フォームの名前’, with: ‘入力する文字列’
→記述してテストコードを実行すると、自動でフォームへ入力出来る。
※フォーム名・要素は検証ツールで確認する
確認方法:検証ツール→左上のカーソルマークのボタンを押す→そのフォームのラベル名(フォームのすぐ上にある表題のような部分)にカーソルを合わせる→<label></label>で囲まれた部分がフォーム名になる
ただし、label内にfor='id名'が記載されている必要がある。このforを記載することでlabelとformを紐づけるため。
また、この紐づけができている場合は、label部分をクリックしてもフォーム内の入力カーソルが活性化する。
----------------------------------------------------------------
【find().click】
find(‘クリックしたい要素’).click
→指定した要素を実際にクリック出来る。
fill_inの時と同じく検証ツールを起動→左上のカーソルマークのボタンを押す→
→クリックしたい要素を確認
例)要素が<input type="submit" name="commit" value="会員登録" class="register-red-btn" data-disable-with="会員登録">となっていた場合
find('input[name="commit"]').clickという記述でクリックできる。
----------------------------------------------------------------
【change】
expect{ 動作 }.to change { モデル名.count }.by(1)
→モデルのレコード数がいくつ変動するのかを確認できる。
つまり、レコードが保存されているかどうかをチェックするということ。
※expectはchangeマッチャでモデルのカウントをする時のみカッコの種類が変わるので注意。
上記のクリックを行なったことでuserモデルのカウントが上がったことを確かめる場合は、↓のように記述する。
----------------------------------------------------------------
【current_path】
現在いるページのパス。
expect(current_path).to eq(root_path)とすると
今いるページがroot_pathであることが確認できる。
----------------------------------------------------------------
【hover】
find(‘ブラウザの要素’).hoverとすると、
特定の要素にカーソルを合わせた時の動作を再現できる。
テストコードで、カーソルを合わせた時に特定の項目が出るかどうかを確認する時に用いる。
----------------------------------------------------------------
【have_link】
指定したリンクがあるかどうかを確認するマッチャ。
----------------------------------------------------------------
【have_selector】
指定したセレクターがあるかどうかを確認するマッチャ。
投稿編集のリンクがあるかどうかや、投稿した内容自体が反映されているかを確認する時に活用する。
結合テストコードで使用した。
----------------------------------------------------------------
【all(‘クラス名’)】
同一のページにある同じ名前の要素をまとめて取り出せる。
aii(‘クラス名’)[添字]とすると、取得する番号を指定できる。
テストコードで、1つのページに同じクラス名が複数存在する時に活用する。
----------------------------------------------------------------
【find_link().click】
a要素で表示されているリンクをクリックできる(a要素のみに用いる)
学習のアウトプット(オリジン間のリソース共有)
【スキーム】
スキームとは、上記のhttp://の部分を指す。
httpsの部分はプロトコルで、一般にhttpプロトコルのことをスキームと呼ぶ。
【ホスト】
http://localhost:3000ではlocalhostの部分
https://www.google.comではwwwの部分を指し、接続する機器のことを表している。
【ポート】
http://localhost:3000では3000の部分を指す。
同じホストでも、ポートを変えれば別のアプリケーションを起動できる。
(接続する側も、同じサーバーでも別のアプリケーションにアクセスできる)
【Origin】
スキーム(プロトコル)・ホスト(ドメイン)・ポートをまとめたものの総称。
URLとの違いは、その後にパスがあるかどうか。
Origin:http://localhost:3000
URL:http://localhost:3000/hoge/fuga
【同一Origin】
パスより前の部分が一致しているものは同一Origin
そうでないものは異なるアプリケーションということになる。
【同一Originポリシー】
異なるOriginのアプリケーションに接続する場合は、セキュリティ面を考慮した制限がかかるというもの。
基本的にはAPIを用いる際に加味することが多い。
例えば自社のアプリケーションに他社の外部APIを利用する場合、
Originが異なるので制限がかかり利用できなくなる。
(制限がないと、意図しないアクセスなどによって損害を被る可能性がある→サーバーアクセスが急増して高額請求となった例など)
【CORS】
Cross Origin Resource Sharingの略でコルスと読む。
異なるオリジンのアプリケーションへアクセスを許可(制限解除)する仕組みのこと。
Ruby on Railsでは、rack-corsというGemを用いて制限解除を行うことができる。
(アクセスされる側のOriginでインストールして、許可するサイトを指定したり、全てのサイトを許可したりする)
学習のアウトプット(Webアーキテクチャ)
【アーキテクチャ】
Webサービスを形作る構造のこと。
Webであれば、構成の元になっているブラウザ・HTTPやHTMLなどがアーキテクチャになる。
【アーキテクチャスタイル】
Webサービスを作る(アーキテクチャを決める)際の設計方針のようなもの。
アーキテクチャスタイルに沿って設計を行うことで、分かりやすく使いやすいサービスとなる。
例えばWEBアプリケーションであればMVCモデル・クライアント・サーバー・オブジェクト志向などがこれに当たる。
例)家の設計
どのような家にするか→アーキテクチャスタイル
家の各部屋やトイレ・玄関など→アーキテクチャ
【REST】
Webのアーキテクチャスタイルの1つで、よく起用される洗練されたもの。
RESTに従って実装を行うことでより良いWebサービスになるが、RESTのうちいくつかを除外したり、RESTに従わないように実装しても問題はない。
Representational State Transfarの略。
(左からそれぞれ「代表的な」「状態」「伝達」の意味)
アーキテクチャスタイルの中で最も有名なもの→クライアント/サーバー
REST→クライアント/サーバーにいくつかの構成を加えたもの
【リソース】
Web上で名前を持ったあらゆる情報。
RESTでは情報をリソースという考え方で表現する。
リソースがどこにあるかを示すのがURLとなる。
=URLを用いてリソースの閲覧をするということ。
例)
リソース:渋谷駅の天気・画像
リソース元URL:Yahoo天気・フリッカー(というサイト)の画像
1つのリソースは複数の情報を持っていたり、変化したりすることもある。
【RESTの構成】
RESTは、クライアント/サーバーを含む6つのアーキテクチャスタイルから構成されている。
・クライアント/サーバー
・ステートレスサーバー
・キャッシュ
・統一インターフェイス
・階層化システム
・オンデマンド
【ステートレスサーバー】
クライアントの状態を管理しないサーバー。(ステートレス=状態がない)
クライアントはリクエストごとに全ての情報を送らなければならないが、サーバー側の実装は簡単で済む。
【キャッシュ】
一度得たリソースをクライアント側で使い回す仕組み。
クライアントとサーバー間の通信回数が減るので、効率化になる。
デメリットとして、古いキャッシュの情報を使い回すことにより、実際の変更が反映されないことがある。
(例えば元のCSSが変更されているのに、キャッシュ内のCSSが古くて画面が変わらない、など)
【統一インターフェイス】
使用するメソッドを固定すること。
例えば通信のHTTPメソッドをGETやPOSTなどに固定することで、全体をシンプルにするなど。
RESTのアーキテクチャスタイルの中で最も特徴的なもの。
【階層化システム】
システムを階層に分けて負担を分散したりするもの。(例えばクライアントとサーバーの間など)
統一インターフェイスの恩恵によって実現できる。
【コードオンデマンド】
JavaScriptのように、プログラムをクライアント側で実行すること。
メリット:クライアントは後から機能を追加していける。
デメリット:クライアントとサーバーでやりとりしているリソースが明確でなくなる。
【SOAP】
Simple Object Access Protocolの略。
通信する上でのルール(規約)で、リクエストやレスポンスをコンピュータが読み取りやすいデータでやり取りすること。
メリット:高機能で拡張性がある
デメリット:構成が複雑で、他のシステムと連携がしにくい
RESTは他のサービスとの連携が容易なので、例えば自身のサイトにGoogleMapを格納したりなどが簡単にできる。(ただしREST自体の拡張性は低い)
SOAPは、複雑な処理をしたり、セキュリティの設定が必要なサービスなどに向いている。
【RESTを使うべき理由】
・構成がシンプル
リソースは全てURLで表現され、それを4つのHTTPメソッド(GET・POST・PUT・DELETE)でやり取りするだけでOK。
・ステートレスで負荷がかかりにくい
ユーザーの情報を保持しないので負荷がかかりにくく、複数のリクエストが集中しても複数のサーバーで対応ができるなど負担を軽減できる。
【RESTful】
RESTの原則に従って実装されている状態のこと。
GoogleやTwitterもRESTfulに実装されている。
また、Ruby on Rails自体もRESTfulな設計になっている。
学習のアウトプット(システム運用の監視)
【監視システム】
サービスが正常に動いているかどうかを自動で確認してくれるシステム。
サービスの提供において最も重要な「サービスが正常に動くこと」を、人の力を使わずに行うことができる。
(人が行うと、膨大な労力が必要かつミスなども起こりやすい)
監視には大きく3つの種類がある。
・正常にサービスを利用できるための監視
・問題を予防するための監視
・発生した問題の原因特定のための監視
【Webシナリオ監視】
結合テストのような工程を本番環境で行なってくれる監視システム。
サービスに正常にアクセスができ、利用できる状態であることを自動で確認してくれる。
【インフラ監視】
アプリケーションそのものではなく、裏側のサーバーなどを監視するシステム。
障害の予測・予防を行なってくれる。
例)2つのサーバーのうち1つがダウンしたことで、もう1つのサーバーに
負荷がかかっており、いつサービス停止してもおかしくない状況を
メールやSlackなどで知らせる
【原因特定のための監視】
これもインフラ監視の仕事の1つ。
システムは、小さなシステム同士のつながりによって成立している。
そのため、1つのシステムだけではなく、各システム間の連携なども併せて監視をすることで、障害の原因などを迅速に特定することができる。
インフラ監視は、システムのつながりも含めた全体を確認し、原因特定にも寄与してくれる。