こんにちは!まさ(@Masao_Sasaki_ae)です!
今、Ruby on Rails を学習中でデバッグ課題を行っていたのですが、その中でも特に詰まってしまったところがあったので、その原因と解決策を共有したいと思います。
※最後に注意事項があるので、全て読んでから実行してください。
エラー内容
エラーを一通り除いてアプリケーションが完成したので、RSpecを使ってテストを行うと

だーっと、95項目中65項目がエラーになってしまいました。。。
教材の通りに作成したので、見た目上の挙動は教材とほぼ同じに仕上がっているはずなのですが、なぜかテスト項目の半分以上がエラーになってしまいました。
原因
ターミナルに表示されたエラーを遡っていくと

あることに気付きました。
>>新規登録に成功する(FILED – 43)
>>ログインに成功する(FILED – 44)
新規登録・ログインに成功しておらず、65項目全てのエラーはログイン後の挙動である気がしたので、ログイン周りを調べてみることにしました。
まずテスト項目の内容については
1 2 3 4 5 6 7 |
it 'ログインに成功する' do fill_in 'user[name]', with: test_user.name fill_in 'user[password]', with: test_user.password click_button 'Log in' expect(page).to have_content 'successfully' end |
2行目と3行目でそれぞれnameとpasswordが指定されています。
これは実際のログイン(新規登録)フォームにname,passwordが指定されていればそのフォームに、その後に書かれているtest_user.name,test_user.passwordが入力されるというものです。
そこで最初にフォームのソースコードを確認しましたが両方ともにちゃんと記述してありましたので、入力に失敗しているということは無いと考えました。
次に”test_user.name”,”test_user.password”という変数ですが、こちらはRSpec自体が元々教材として用意されていたファイルでしたので中身は確認していませんが、テストコードの作成はカリキュラム外でしたので、メタ読みで変数に間違いは無いと仮定して後回しにしました。
次にユーザーのデータベースの確認を行いました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
create_table "users", force: :cascade do |t| t.string "email", default: "", null: false t.string "name" t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" t.text "introduction" t.string "profile_image_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end |
・・・ん?
一箇所、怪しい行がありますね。
3行目のnameの設定項目に”デフォルト”と”制約”が設定されていませんね。
2行目のemailにはあるのに3行目のnameにはそれがありません。
default: “” は初期値の設定ですが””中に文字を入れるとそれが入力された状態となります。今回は””の中は何も無いので空欄になります。
null: false は空欄のまま登録(ログイン)をしようとするとエラーになります。nullは空っぽという意味でそれがfalseなので空欄を認めないということになります。(falseではなくtrueなら空欄でもOKということになります。)
user.rbファイルではバリデーションでnameフォームの空欄を禁止にしていますが、テーブルのnameカラム内の空欄は特に制約していませんでした。
nameフォームさえ空欄禁止にしていればnameカラムの方は空欄の設定はなくてもいい気はしますが、脆弱性を作ってしまう可能性があるので、ここは設定を追記(後述)していきます。
ちなみに、今回利用しているdeviseはnameをログインには利用しない設定になっています。
しかし、今回のアプリケーションではログイン情報としてnameも利用するのでその設定は変えたつもりではいたのですが、カラムの設定までできていませんでした。
対策
これがエラーの直接原因になっているかは定かではありませんが、ログイン周りの設定不足は疑われるのでとりあえず修正してみることにしました。
今回の修正はマイグレーションファイルを直接変更するやり方です。(最後にテーブルのリセットも行うのでこれまでのテーブルに登録された情報は消えます)
まずは
で現在のマイグレーションファイルの状況を確認します。
————————————————–
up 20200611082923 Devise create users
up 20200611083210 Create books
マイグレーションファイルは2種類あったのですが、どちらもサーバー上にある(up)のが確認できます。
これを編集するには、ファイルを一旦ローカルに下ろす必要があり、それが次のコマンドです。
このコマンドでファイルを一旦ローカルに降ろせます。
もう一度
で確認してみましょう。
————————————————–
up 20200611082923 Devise create users
down 20200611083210 Create books
rollbackでは最新のマイグレーションファイル1つしか操作できないので、過去分を操作する場合はそのファイルまでの数だけコマンドの後ろにSTEP=nのオプションをつけてコマンドを実行します。
今回の場合だと2つ前のマイグレーションファイル(users)をローカルで編集したいので、STEP=2となります。
こうすると
————————————————–
down 20200611082923 Devise create users
down 20200611083210 Create books
となるので、これで2つ目のマイグレーションファイル(users)の内容を変更します。
過去分1つだけを単独でdownさせることはできません。
目的のマイグレーションファイルまでのファイルは一旦全てdownさせなければならない仕様です。
マイグレーションファイルは通常、直接変更を加えるよりもマイグレーションファイルを追加することでカラムに変更・削除を加えるものです。
個別にdownできるようにしてしまうと、それ以降に追加されていたマイグレーションファイルとの整合性が取れなくなってしまうからだと推測します。
(例:
- 1個目のマイグレーションファイルで:namaeカラムの作成
- 2個目のマイグレーションファイルで:namaeカラムの削除
- 3個目のマイグレーションファイルで:nameカラムの作成
- 1個目のマイグレーションファイルの:namaeカラムを本文のやり方で:nameに直接変更
すると2個目のマイグレーションファイルで:namaeカラムを削除しようとしていますが、すでに:namaeカラムというのは存在しなくなっているので、
$ rails db:migrate
をするタイミングでエラーとなります。
今回ですと
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
create_table "users", force: :cascade do |t| t.string "email", default: "", null: false t.string "name", default: "", null: false #ここを追記 t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" t.text "introduction" t.string "profile_image_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end |
3行目のnameのオプションにdefault: “”, null: falseを追記することになります。
ファイルの編集が完了したらそのままでは反映されないので
で再度サーバー上にアップロードします。
念のため、
で、マイグレーションファイルの状態を確認をしておきます。
————————————————–
up 20200611082923 Devise create users
up 20200611083210 Create books
これでどちらのファイルもサーバー上にアップロードできていることが確認できます。
今回の場合必要ないかもしれませんが、
をすることで、全てのテーブルのデータを一旦リセットしておきます。
(テーブル個々にresetする方法は調べていないので必要な方は各自でお願いします。)
注意
今回のやり方は通常の変更方法では無いので、チームで開発をされている方にはあまりおすすめできません。
自分の場合は個人開発でやっていること、また、今回はオプションの追加のみで、カラムの追加や削除ではなかったので直接マイグレーションファイルを変更する手段を取りました。
通常のマイグレーションファイルを追加する方法では履歴が残るので、[いつ][どのカラム]に[どのような変更]を加えたかをファイル名と内容で確認することができます。
しかし、今回のようにマイグレーションファイルを直接触ってしまうと第三者が確認した時に、過去にどこに変更点があったかを知る術が無いため、データベースにバグが起きた時に解決するのが困難になってしまいます。
どうしても今回のように直接編集が必要な場合はコードの修正箇所の近くにコメントをつけるなどして、明示することをおすすめします。
まとめ
エラー発生から解決までさらっと解説しましたが、実際はこのエラーを解決するのに半日ほどかかっています・・・(笑)
以前メンターさんに「テスト段階でかなりエラーが出る時はログイン機能に不具合があるのでそこを気をつけてみてください」と言われていたのですが、その課題の時はテストでそのエラーは出なかったんですよね
今回の課題で初めて目の当たりにし、そしてこの記事を書いている前日の金曜日はメンターさんへの質問が唯一できない曜日だったので、自分ひとりで一日格闘していました(笑)
どなたかのお役に立てたら幸いです。
この記事が参考になりましたら、記事のシェアやTwitterのフォローをしていただけると嬉しいです。