1限目be動詞とは
はじめに
プログラミング学習において、英語の必要性を感じたため英語を学習しています.
英語アレルギーですが、英語力を身に着けるためアウトプットします.
概要
be動詞とは
使い方(法則)
これを見ればもう迷わないフローチャート
be動詞とは
is、am、areのことを言う。日本語でいうと述語
主語によってbe動詞が変化する.
文の中では主語の直後に来る.
使い方
①使い方(法則)
主語 + be動詞
文の中では主語の直後に来る.(再記)
I am a new student. | 私は新しい生徒です. |
You are my friend. | あなたは私の友達です. |
Mike is in Tokyo now. | マイクは今、東京にいる. |
My bag is on the desk. | 私のカバンは机の上にある. |
フローチャート
学習に使っているサイト
サンプルデータbuild、createどちらを使うか
概要、目的
サンプルデータの使い分け
テスト作成時に手こずったのでアウトプットも兼ねて
前提
使用するアプリのログイン機構はsorceryを使用
テストはrspec
FactoryBotを使用
今回使用するコード
# spec/factories/users.rb FactoryBot.define do factory :user do sequence(:email){|n| "test@#{n}example.com" } password { 'password' } password_confirmation { 'password' } end end _____________________________________________________________________ # spec/features/users_spec.rb RSpec.feature "Users", type: :feature do describe 'ログイン前' do # 変化させる箇所 create,buildした時の違い # ①let(:user) { create(:user) } # ②let(:user) { build(:user) } context 'フォームの入力値が正常' do it 'ユーザーの新規登録作成が成功する' do visit root_path click_link 'SignUp' fill_in 'Email', with: user.email fill_in 'Password', with: user.password fill_in 'Password confirmation', with: user.password_confirmation # ③byegug デバック時に使用 click_button 'SignUp' expect(page).to have_content 'User was successfully created.' end end end end
実現したいこと
図1のフォームが正常に動作し、ログインできるかのテスト
create,buildの使い分け
今回はサンプルデータをcreate,buildしてみてどのような違いがあるか、試していきます.
createしてテストすると...
結論から言うとcreateではテストはパスしません.
最初私はcreateしたデータで、テスト項目に当てはめればいいと思い、テストを作成しました.
今後テストを書いていく上で、同じ失敗を繰り返さないためにもアウトプットします.
上のコード①を有効にしてテストを実行します.
テスト結果
$ bundle exec rspec 1) Users ログイン前 フォームの入力値が正常 ユーザーの新規登録作成が成功する Failure/Error: expect(page).to have_content 'User was successfully created.' expected to find text "User was successfully created." in "Login SignUp\nSignUp\n3 errors prohibited this user from being saved:\nPassword is too short (minimum is 3 characters) Password confirmation can't be blank Email has already been taken\nEmail\nPassword\nPassword confirmation\nBack" # ./spec/features/users_spec.rb:15:in `block (4 levels) in <main>' Finished in 0.44038 seconds (files took 1.64 seconds to load) 18 examples, 1 failure, 8 pending Failed examples: rspec ./spec/features/users_spec.rb:7 # Users ログイン前 フォームの入力値が正常 ユーザーの新規登録作成が成功する
原因
どうなっているか上のコード③を有効にして再度テストを実行します.
userそのものは存在してるようです.
(byebug) user
#<User id: 1, email: "test@1example.com", crypted_password: "$2a$04$gk9IxtIsYvQIjxcluZv8R.iEycIUUU0DzF6EJMu62XC...", salt: "vD43wtyV-oauxVTaFAT_", created_at: "2021-02-26 02:49:46", updated_at: "2021-02-26 02:49:46">
各カラムに値があるか見ていきます.
(byebug) user.email "test@1example.com" (byebug) user.password nil (byebug) user.password_confirmation nil (byebug)
デバックの結果passwordカラムの値がnilのため、テスト時にnilの値が入力されたためにユーザー登録ができないことがわかります.
エラーメッセージを再度見直すと、パスワードが空だと言われています.
1) Users ログイン前 フォームの入力値が正常 ユーザーの新規登録作成が成功する Failure/Error: expect(page).to have_content 'User was successfully created.' expected to find text "User was successfully created." in "Login SignUp\nSignUp\n3 errors prohibited this user from being saved:\nPassword is too short (minimum is 3 characters) Password confirmation can't be blank Email has already been taken\nEmail\nPassword\nPassword confirmation\nBack" # ./spec/features/users_spec.rb:15:in `block (4 levels) in <main>'
ではなぜpasswordが空なのでしょうか?
コンソールでcreateした時と、buildした時の違いを見てます.
#create時 [1] pry(main)> create_user=FactoryBot.create(:user) (0.1ms) SAVEPOINT active_record_1 User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "test@1example.com"], ["LIMIT", 1]] User Create (2.1ms) INSERT INTO "users" ("email", "crypted_password", "salt", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["email", "test@1example.com"], ["crypted_password", "$2a$10$FooeA7t.Wprve0Z7JhgZsuryfRypjCT7dcIUjJzSIwWvronJ7Q2ue"], ["salt", "_uQ2mDDruScW1H6sxDy7"], ["created_at", "2021-02-26 03:20:14.766456"], ["updated_at", "2021-02-26 03:20:14.766456"]] (0.1ms) RELEASE SAVEPOINT active_record_1 => #<User:0x00007fa18bb05610 id: 7, email: "test@1example.com", crypted_password: "$2a$10$FooeA7t.Wprve0Z7JhgZsuryfRypjCT7dcIUjJzSIwWvronJ7Q2ue", salt: "_uQ2mDDruScW1H6sxDy7", created_at: Fri, 26 Feb 2021 03:20:14 UTC +00:00, updated_at: Fri, 26 Feb 2021 03:20:14 UTC +00:00> #build時 [4] pry(main)> build_user=FactoryBot.build(:user) => #<User:0x00007fa18b341b40 id: nil, email: "test@2example.com", crypted_password: nil, salt: nil, created_at: nil, updated_at: nil>
create時にはcrypted_passwod、saltの値が入っていて、build時にはnilということがわかります.
次にFactoryBotで定義されている値が入っているか確かめてます.
FactoryBotのコード(再掲載)
FactoryBot.define do factory :user do sequence(:email){|n| "test@#{n}example.com" } password { 'password' } password_confirmation { 'password' } end end # ここからコンソール上のコードと比べながら # create_user = FactryBot.create(:user) # build_user = FactryBot.build(:user) # create時 [7] pry(main)> create_user.email => "test@1example.com" [8] pry(main)> create_user.password => nil [9] pry(main)> create_user.password_confirmation => nil #build時 [10] pry(main)> build_user.email => "test@2example.com" [11] pry(main)> build_user.password => "password" [12] pry(main)> build_user.password_confirmation => "password" [13] pry(main)>
テスト失敗時デバックした時と同じように、createするとpasswod,password_confirmationがnilになっています.
反対にbuildではFactryBotで定義した通りに、password,password_confirmationカラムに値が入っています.
create時はpasword,password_confirmationがnilになり、build時には値が入っていることがわかりました.
ではなぜcreate時はnilになるのでしょうか?
再度こちらのコードを見てみましょう.
#create時 [1] pry(main)> create_user=FactoryBot.create(:user) (0.1ms) SAVEPOINT active_record_1 User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "test@1example.com"], ["LIMIT", 1]] User Create (2.1ms) INSERT INTO "users" ("email", "crypted_password", "salt", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["email", "test@1example.com"], ["crypted_password", "$2a$10$FooeA7t.Wprve0Z7JhgZsuryfRypjCT7dcIUjJzSIwWvronJ7Q2ue"], ["salt", "_uQ2mDDruScW1H6sxDy7"], ["created_at", "2021-02-26 03:20:14.766456"], ["updated_at", "2021-02-26 03:20:14.766456"]] (0.1ms) RELEASE SAVEPOINT active_record_1 => #<User:0x00007fa18bb05610 id: 7, email: "test@1example.com", crypted_password: "$2a$10$FooeA7t.Wprve0Z7JhgZsuryfRypjCT7dcIUjJzSIwWvronJ7Q2ue", salt: "_uQ2mDDruScW1H6sxDy7", created_at: Fri, 26 Feb 2021 03:20:14 UTC +00:00, updated_at: Fri, 26 Feb 2021 03:20:14 UTC +00:00> #build時 [4] pry(main)> build_user=FactoryBot.build(:user) => #<User:0x00007fa18b341b40 id: nil, email: "test@2example.com", crypted_password: nil, salt: nil, created_at: nil, updated_at: nil>
create時にはpassword,password_confirmationカラムの値がnilに対して,crypted_passwordというカラムに値が代入されています.
これはpasswordをハッシュ化した値が代入されています.
ハッシュ化することで、悪意を持ったユーザーからpasswordを盗まれないようにするためです.
つまりnilになるのはsorceryのコードが関係しています.
corceryではユーザーが入力したpassword,password_confirmationカラムの値を同一か確認し、さらにpasswordをハッシュ化しcrypted_passwordに保存しています.
crypted_passwordに値が代入されたタイミングでpassword,password_confitmationにnilをセットしています.
nilをセットしているのは、悪意を持ったユーザーから身を守るためです.(直接データベースからカラムにアクセスされた際の対策)
以上がcreate時にpassword,password_confirmationがnilになり、テストがパスしない原因です.
最後にテストをbuildしたサンプルデータでテストをパスしましょう.
buildしてテスト
テストコード①を無効、②を有効にして、テストを実行すると成功します.
ログイン機構などをgemを利用していると、このようなことがあるので注意しましょう.
またそもそも今回のテストは新規登録が成功するかのテストなので、createしたユーザーでテストをするのはテストの意味が薄れてしまう.(実際のアプリの動作とは異なる)ので書いているテストが、実際のアプリの挙動と同じかどうかを確認しながら書きましょう.
FactoryBot関連付け注意点、余計なデータが作成されてしまう仕組み
概要
関連付け、複数のサンプルデータの定義の仕方 + 呼び出し方
前提条件
・gemインストール済み.
・FactoryBotサンプルデータの定義の仕方、呼び出し方がわかる.
・↓このブログの内容が理解できる(5分ほどで読めます.)
FactoryBotの関連付け(注意点)
なお関連付けは以下の通りとする.
- 余計なデータを作成してしまう例
FactoryBot定義
spec/factories/ ├── notes.rb ├── projects.rb └── users.rb #user.rb FactoryBot.define do factory :user, aliases: [:owner] do first_name "Aaron" last_name "Sumner" sequence(:email) { |n| "tester#{n}@example.com" } password "dottle-nouveau-pavilion-tights-furze" end end #project.rb FactoryBot.define do factory :project do sequence(:name) { |n| "Project #{n}" } description "A test project." due_on 1.week.from_now association :owner end end #note.rb FactoryBot.define do factory :note do message "My important note." #FactoryBot.create(:project)と等価 association :project #FactoryBot.create(:user)と等価 association :user end end
noteに関するテスト
require 'rails_helper' RSpec.describe Note, type: :model do it 'noteのサンプルデータをFactoryBotを使用して呼び出してみる' do note = FactoryBot.create(:note) #関連付けを使用してオブジェクトを呼び出してみる puts "#{note.project.user_id}" puts "#{note.user_id}" end end
上のテストで期待することはどちらも同じuser_idが返ってくることです.
$ bin/rspec Note 1 2
テスト結果からわかることは、userが2人分作成されていることです. なぜ2人作成されてしまうのでしょうか? コードを順に見ていきましょう.
_____________________________________________________________________ ① require 'rails_helper' RSpec.describe Note, type: :model do it 'noteのサンプルデータをFactoryBotを使用して呼び出してみる' do note = FactoryBot.create(:note) #関連付けを使用してオブジェクトを呼び出してみる puts "#{note.project.user_id}" puts "#{note.user_id}" end end _____________________________________________________________________ ②,③ #note.rb FactoryBot.define do factory :note do message "My important note." #FactoryBot.create(:project)と等価 association :project #FactoryBot.create(:user)と等価 association :user end end ____________________________________________________________________ ④,⑤ #project.rb FactoryBot.define do factory :project do sequence(:name) { |n| "Project #{n}" } description "A test project." due_on 1.week.from_now association :owner end end
① FactoryBot.create(:note)が実行されると、spec/factories/notes.rbの内容が実行されます.
② message "My important note"が属性値として、代入されます.
③ association :projectでspec/factories/projectsが呼び出されます.つまりFactoryBot.create(:project)が実行されます.
④ name,description,due_onそれぞれカラムの値が設定されます.
⑤ association :ownerが実行されると、spec/factories/usersが呼び出されます.
つまりFactoryBot.create(:users)が実行され、一人目のuserが作成されました.(コードは省略します.)
※association :userではなく、association :ownerなのはモデルでuser_idではなく、owner_idになるように設定しているからです.
ここは今回の内容と関係ないので、association: ownerはassociation: userと同じことをしているという理解で大丈夫です.
下のコードが設定をしているコードです.
class Project < ApplicationRecord belongs_to :owner, class_name: 'User', foreign_key: :user_id end
すこし脱線しましたが、②のコードをもう一度見てみましょう.
association :userが記述されています.
このコードが実行されると、userが当たらに作成されますよね.
しかしすでに、⑤によってuserは作成されています.そうです!これが2人のuserが作られてしまう仕組みです.
2人のユーザが作られないようにするには,したコードのようにすれば防げます.
#note.rb FactoryBot.define do factory :note do message "My important note." #FactoryBot.create(:project)と等価 association :project # 上のassociation :projectで作成されたユーザを渡してあげる user: { project.owner } end end
テスト実行
$ bin/rspec Note 1 1
期待通りになりました.
まとめ
FactoryBotを使用すると、気づかないうちに余計なデータが作成されてしまう.
余計なデータが作成されてしまうとテストに時間がかかるため、適宜チェックしよう.
【rspec】FactoryBot サンプルデータの定義の仕方
概要
テストで使用するサンプルデータの定義の仕方 + 呼び出し方
前提条件
・gemはインストール済み.
・rspecを使用する.
・config/application.rb内generatetorの設定欄に、fixtures: falseが記述されていないこと.
・もしくは下のコードの通りにすること.
Bundler.require(*Rails.groups) module Projects class Application < Rails::Application config.load_defaults 5.1 config.generators do |g| g.test_framework :rspec, # ビュースペックを作成しない. view_specs: false, # ヘルパーファイル用のスペックを作成しないこと. helper_specs: false, # ルーティング用のスペックを作成しない. routing_specs: false end end end
ファイル生成
・文法 $ bin/rails g factory_bot:model モデル名 ・例: $ bin/rails g factory_bot:model user
・生成されるファイルの場所.
spec/factories/ ├── notes.rb ├── projects.rb └── users.rb
定義の仕方
・ジェネレーターで作成されたファイルに属性値などを定義する.
FactoryBot.define do # aliases: [:owner]はまだわからなくて良い、次回以降の記事で解説 factory :user, aliases: [:owner] do first_name "Aaron" last_name "Sumner" # 呼び出されるたびにnの数が増えていくため、emailがユニークになる. sequence(:email) { |n| "tester#{n}@example.com" } password "dottle-nouveau-pavilion-tights-furze" end end
・テスト内で呼び出す
# FactoryBot使用しない場合 user = User.create( first_name: "Joe", last_name: "Tester", email: "joetester@example.com", password: "dottle-nouveau-pavilion-tights-furze", ) # FactoryBot使用する場合 FactoryBot.create(:user) FactoryBot.build(:user) # 特定の属性値のみ変更したい場合 FactoryBot.create(:user, email: nil) FaactryBot.build(:user, name: 'foobar')
今日はここまで
作成時間30分
次回以降 関連付け、より複雑なサンプルデータ