2021年10月8日 JavaScript (JavaScript Primer) 変数と宣言
varの問題
同じ変数名で、再定義できてしまう点です。
letやconstでは、同じ名前の変数を再定義しようとすると、次のような構文エラー(SyntaxError)が発生します。
let word = "モスバーガー"; let word = "モスバーガー"; // SyntaxError: Identifier 'word' has already been declared
const food = "マック"; const food = "マック"; // SyntaxError: Identifier 'food' has already been declared
そのため、間違えて変数を二重に定義してしまうというミスを防ぐことができます。
またvarには変数の巻き上げと呼ばれる意図しない挙動があります。
後の章で詳しく学習するようなので、現時点では「letはvarを改善したバージョン」ということだけ覚えておくとよいと本書では言っています。
上記の理由から、varではなくlet constを使うべきです。
後方互換性を考慮し、varは使用可能になっています。
letとconstの違い
- constは、再代入できない変数を宣言できます
- letは、再代入ができる変数を宣言できます
constは再代入できない変数を定義するキーワードです。
再代入を禁止することで、ミスから発生するバグを減らすことが期待できます。
このため変数を宣言する場合には、まずconstで定義できないかを検討し、できない場合はletを使うことを推奨しています。
変数名の命名規則
変数を作成するときは、命名規則を守る必要があります。 JavaScript Primerに記載されている命名ルールを以下に引用します。
- 半角のアルファベット、_(アンダースコア)、$(ダラー)、数字を組み合わせた名前にする
- 変数名は数字から開始できない
- 予約語と被る名前は利用できない
変数名の最初の文字が数字でしたり、変数名が数字のみの場合は、変数宣言に失敗します。
const 2is = 0; // SyntaxError: Invalid or unexpected token const 123 = "あいう" // SyntaxError: Unexpected number
また、予約語として定義されているキーワードは変数名として利用できません。予約語とは、あらかじめ使い方が決まっている単語です。
const for = 10; // SyntaxError: Unexpected token 'for'
constは厳密には定数ではない
本書の内容を噛み砕いて書いてみました。
1.定義したものが、可変な値(配列など) だった場合は、その中身となるものは書き換えが可能なので厳密には定数とは呼べないと言えます。
2.定義したものが、不変な値(文字列や数値) だった場合は、変更できないため定数と言えます。
ちなみに、同じ入力に対して常に同じ出力が保障されることを参照透過性といいます。
1の場合のコード例
const array = []; array.push(5); array.push(7); array.push(7,8); console.log(array); // [ 5, 7, 7, 8 ]←arrayは可変な値のため中身を書き換えられています。(定数とは言えない)
2の場合のコード例
const number = 10; // 10は数値であり、不変な値のため変更することができません。(定数と言えます)
まとめ
- constは、再代入できない変数を宣言できる
- letは、再代入ができる変数を宣言できる
- varは、再代入ができる変数を宣言できるが、いくつかの問題が知られている
- 変数の名前(識別子)には利用できる名前のルールがある
- constは厳密に言うと定数ではない
参照
2021年10月7日 JavaScript (JavaScript Primer) 基本文法
JavaScriptとは
JavaScriptはウェブブラウザ上で動くプログラミング言語です。
JavaScriptはECMAScriptの仕様に従って動作しています。 ECMAScriptは毎年アップデートされます。2015年に大きくアップデート(ES2015)してJavaScriptは再度注目されるようになりました。
JavaScriptとECMAScriptの関係とは?
JavaScriptという言語はECMAScriptという仕様によって動作が決められています。ECMAScriptという仕様では、どの実行環境でも共通な動作のみが定義されているため、基本的にどの実行環境でも同じ動作をします。
画像引用 つまり、ECMAScriptは、どの実行環境でも共通している部分の動作を担うもので、JavaScriptは、実行環境によって異なる部分のある機能も含んだものとなっています。JavaScriptがECMAScriptを内包しているとイメージすると良いです。
基本的な文法
・ 大文字と小文字は区別する
// `name`という名前の変数を宣言 const name = "azu"; // `NAME`という名前の変数を宣言 const NAME = "azu";
・ 「文」はセミコロンで区切る
// 式や文の間にスペースがいくつあっても同じ意味となる
1 + 1;
1 + 1;
実行コンテキストとは
実行コンテキスト、スコープチェーン、JavaScript内部に詳しく記載されているで、引用します。
実行コンテキスト(EC) は、JavaScriptコードが実行される環境として定義されます。環境とは、JavaScriptコードが特定の時間にアクセスできるthis、変数、オブジェクト、および関数の値を意味します。
strict mode
use strict
という文字列をファイルまたは関数の先頭に書くことで、そのスコープにあるコードはstrict modeで実行されます。
strict modeでは開発者が安全にコードを書けるようになっています。例えば、過去の技術や仕組みで構築されているシステムの機能や構文などを禁止したり、問題を含んだコードに対しては例外を投げることで間違いに気づきやすくしてくれる機能を提供しています。
"use strict"; // このコードはstrict modeで実行される
JavaScriptでのコメントの書き方
JavaScriptでもプログラムとしては評価されないが、ソースコードの説明などを付け足せるコメント機能があります。コメントには、1行コメントと複数行コメントがあります。
// 一行コメント // この部分はコードとして評価されない
/* 複数行コメント 囲まれている範囲がコードとして評価されない */
//HTML-likeコメント <!-- この行はコメントと認識される console.log("この行はJavaScriptのコードとして実行される"); --> この行もコメントと認識される
上記の HTML-likeコメント とは、JavaScriptがサポートされていないブラウザでscriptタグを実行するためのコメントです。ES2015から追加されましたが、現在では全てのブラウザがJavaScriptに対応しているので、このコメント機能は不要です。
<script>
<!-- この行はコメントと認識される
console.log("この行はJavaScriptのコードとして実行される");
--> この行もコメントと認識される
</script>
コメントに関するまとめ
//
以降から行末までが1行コメントです。/*
と*/
で囲まれた範囲が複数行コメントです。- HTML-likeコメントは後方互換性のためだけに存在します。
参照
JavaScript Primer
JS-JavaScriptでコメントを記述する&コメントアウト活用法
実行コンテキスト、スコープチェーン、JavaScript内部
2021年10月7日 現場Rails Chapter5-16 Specが失敗したときの調査方法
Specが失敗した時の調査手順
Specが失敗した時に、以下の手順でエラーの原因を特定します。 手順3に関しては、モデルが正常に動作しているかを確認したい時に実行します。
失敗場所とエラーメッセージを確認する。
失敗場所とエラーメッセージを手がかりに原因を特定する。
コンソールを使ってモデルの動きを確認する。
スクリーンショットを活用する。
擬似的にSpecを失敗させる
今まで通っていたSpecが失敗するときは、プロダクトコードを変更したときが多いと考えられるので、擬似的に同様の状況を再現するためにapp/models/tasc.rbのバリデーションをコメントアウトします。
class Task < ApplicationRecord # validates :name, presence: true, length: { maximum: 30 }
これにより、通っていたテストが通らなくなり、実際にコード変更を行なったときと同様のエラーメッセージを出せました。
コンソールのsandboxモードについて
コンソールのsandboxモードを使うと、コンソール内でデータベースの内容を変更しても最終的にロールバックして、起動前の状態に戻します。
※sandboxの語源としては、砂場です。気軽に失敗のできる環境という意味合いです。
スクリーンショットを活用する
Specが失敗した際はスクリーンショット保存されるため確認することも出来ます。 ターミナル上で以下のようにエラー文に保存先が出力されます。
[Screenshot]: tmp/screenshots/ファイル名
スクリーンショットを見ることで得られる情報は多いため、原因調査の際にスクリーンショットを確認することは有効な手段ですので活用してみましょう。
参考
2021年10月5日 現場Rails Chapter5-12 詳細表示機能のSpecを追加する
詳細表示機能Specを追加する
前回の一覧表示機能に続いて、詳細表示機能のテストコードを追記します。
describe 'タスク管理機能', type: :system do let!(:task_a) { FactoryBot.create(:task, name: '最初のタスク'), user: user_a)} ...省略 describe '詳細表示機能' do context 'ユーザーAがログインしているとき' do let(:login_user) { user_a } before do visit task_path(task_a) end it 'ユーザーAが作成したタスクが表示される' do expect(page).to have_content '最初のタスク' end end end end
let!を用いてtask_aを定義しておくと、タスク作成処理を共通化することが出来るため、一覧表示機能でタスクを作成する処理を記述する必要がなくなります。
shared_examplesを利用する
共通したitの内容をまとめる事ができる機能です。
今回の例の場合、詳細表示機能のテストと一覧表示機能のテストで
itの内容が被ってしまっています。
その場合に、以下のようにshared_examplesを用います。
shared_examples_for 'ユーザーAが作成したタスクが表示される' do it { expect(page).to have_content '最初のタスク' } end
このように共通化の記述をした上で、これまでitを書いていた場所には以下のように書きます。
it_behaves_like 'ユーザーAが作成したタスクが表示される'
使用上の注意としては、すぐに理解できる名前にし、可読性を損なわないようにする必要があります。
新規作成機能のSystem Spec
describe 'タスク管理機能', type: :system do describe '新規作成機能' do let(:login_user) { user_a } before do visit new_task_path fill_in 'Name', with: task_name click_button '登録する' end context '新規作成画面で名称を入力したとき' do let(:task_name) { '新規作成のテストを書く' } it '正常に登録される' do # .alert-successというCSSクラスを指定している # 特定のタグやCSS要素に特定の文字列が表示されていることを検証する expect(page).to have_selector '.alert-success', text: '新規作成のテストを書く' end end context '新規作成画面で名称を入力しなかったとき' do let(:task_name) { '' } it 'エラーとなる' do # 検証エラーを表示する領域内に、「名称を入力してください」というエラーメッセージが表示される。 within '#error_explanation' do expect(page).to have_content '名称を入力してください' end end end end end
withinメソッド
画面内の特定の範囲に絞って検証することが出来ます。 '名称を入力してください'というエラーメッセージが画面内に1つだけしか存在しない場合などを想定した上でerror_explanationというidの要素を指定して検証しています。
letの上書き
同じ名前のletを複数回定義すると、常に下の階層に定義したletが使われます。つまり、letは上書きができます。しかし、同じ名前のletを複数回定義すると、最終的にどのletが使われるのか分からなくなります。そのため、適切なバランスでletを使います。
describe 'タスク管理機能', type: :system do describe '新規作成機能' do let(:login) { user_a } let(:task_name) { '新規作成のテストを書く' } # デフォルトとして設定 before do ... end context '新規作成画面で名称を入力したとき' do it '正常に登録される' do expect(page).to have_selector '.alert-success', text: '新規作成のテストを書く' end end context '新規作成画面で名称を入力しなかったとき' do let(:task_name) { '' } # 上書き it 'エラーになる' do within '#error_explanation' do expect(page).to have_content '名称を入力してください' end end end end end
参照
2021年10月4日 現場Rails Chapter5-9 他のユーザーが作成したタスクが表示されないことの確認
前回はユーザーAが作成したタスクがページに表示されていることを確認するテストを書きました。 今回はユーザーBがログインした際にユーザーAのタスクは表示されていないことを確認するテストを書いていきます。
require 'rails_helper' describe 'タスク管理機能', type: :system do describe '一覧表示機能' do before do # user_aを名前とメールを指定して、新たに作成する user_a = FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com') # user_aに紐づくタスクをタスク名を指定して作成する FactoryBot.create(:task, name: '最初のタスク', user: user_a) end context 'ユーザーAがログインしているとき' do before do # ログインページへアクセスする visit login_path # メールアドレスとパスワードをログインフォームに入力する fill_in 'メールアドレス', with: 'a@example.com' fill_in 'パスワード', with: 'password' # ログインボタンを押す click_button 'ログインする' end it 'ユーザーAが作成したタスクが表示される' do # pageに最初のタスクという内容があることを期待する。 expect(page).to have_content '最初のタスク' end ーーーーーーーーーーーーーーーーーーーーーーー # ここからが今回学んだ内容になります。 context 'ユーザーBがログインしているとき' do before do Factorybot.create(:task, name: 'ユーザーB', email: 'a@example.com', user: user_a) visit login_path fill_in 'メールアドレス', with: login_user.email fill_in 'パスワード', with: login_user.password # ログインボタンを押す click_button 'ログイン' end context 'ユーザーAがログインしているとき' do let(:login_user){ user_a } it 'ユーザーAが作成したタスクが表示される' expect(page).to have_content '最初のタスク' end end # ユーザーBとしてログインしている為、ユーザーAが作成した最初のタスクが表示されないようにする it 'ユーザーAが作成したタスクが表示されない' do # '最初のタスク'が表示されない。 expect(page).to have_no_content '最初のタスク' end end end end
beforeを利用した共通化
コード自体の共通点が多い時、似たコードをまとめて、共通化することができます。今回は、beforeを使って共通化していきます。
describe 'タスク管理機能', type: :system do describe '一覧表示機能' do let(:user_a){ FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com')} let(:user_b){ FactoryBot.create(:user, name: 'ユーザーB', email: 'b@example.com')} before do FactoryBot.create(:task, name: '最初のタスク', user: user_a) # ここでletのユーザーAが実際にデータベースに登録されます。 visit login_path fill_in 'メールアドレス', with: login_user.email fill_in 'パスワード', with: login_user.password click_button 'ログインする' end context 'ユーザーAがログインしている時' do let(:login_user) { user_a } it 'ユーザーAが作成したタスクが表示される' do expect(page).to have_content '最初のタスク' end end context 'ユーザーBがログインしている時' do let(:login_user) { user_b } it 'ユーザーAが作成したタスクが表示されない' do expect(page).to have_no_content '最初のタスク' end end end end
否定系の書き方
以下の3種類がありますがどれを使用しても大丈夫です。
expect(page).to have_no_content '最初のタスク' expect(page).not_to have_content '最初のタスク' expect(page).to_not have_content '最初のタスク'
伊藤淳一さんはこちらのツイートで to_not
が書きやすいとおっしゃっているので参考にしてみても良いかも知れません。
上記のコード(否定系の書き方の上)のように、同じ階層にあるすべてのcontext~end内で共通する処理は、一つ上の階層内で、beforeを定義して、共通処理を書くことが出来ます。
この場合は、context処理前にbeforeブロック内の処理が実行されます。ここで実行される処理は、パスワードやメールなどの値は実際に入っておらず、箱のような状態になっています。実際にcontext処理内のletで、before処理内のパスワードやメールの値が入ります。
letとlet!
現場Rails P.213~P.214に詳しく記載されてるので、以下に引用します。
letは呼び出されたタイミングで実行され、一度も呼び出されないときは実行されずじまいとなります。
そのため、例えばlogin_userというletを定義したとしても、以下の例のようにlogin_userを一度も呼び出さずにログインすると、ユーザーが作られることがありません。そのため、ログインが失敗してしまいます。
describe '一覧表示機能' do let(:login_user) { FactoryBot.create(:user, name: 'ユーザーA, email: 'a@exaple.com)} before do # let(:login_user)の定義を一度も呼び出さずにログインする visit login_path fill_in 'メールアドレス', with: 'a@example.com' fill_in 'パスワード', with: 'password' click_button 'ログインする' #=> ユーザーAが作られていないためにログイン失敗する end ... end
このような場合は、letの代わりにlet!を利用すれば、beforeの前にユーザーが登録されるため、意図通りにログインできるようになります(参照しないのであればlet!にもせずにbeforeに記述しても良いという考え方もできますが、let!を使う方が読みやすくなる場合があります。また「呼び出されるケースと呼び出されないケースがあるが、データは常に作りたい」といった場合にも便利に利用できます)
参照
現場Rails(p.205~214)
RSpecで「~ではないこと」を検証するときは expect(x).to_not 、または expect(x).not_to のどちらを使うべきか?
2021年10月1日 現場Rails Chapter5-8 タスクの一覧表示機能のSystem Spec
タスクの一覧表示機能のSystem Spec
タスク一覧表示機能をテストする場合、以下のようなSystem Specを書きます。
ここではファクトリーに定義したテストユーザーをそのまま使わずに、"ユーザーA"を定義しています。今後、ユーザーBなど別のユーザーを扱う際にスムーズにするためです。
require 'rails_helper' describe 'タスク管理機能', type: :system do describe '一覧表示機能' do before do # ユーザーAを作成 # 作成者がユーザーAであるタスクを作成しておく user_a = FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com') FactoryBot.create(:task, name: '最初のタスク', user: user_a) end context 'ユーザーAがログインしているとき' do before do # ユーザーAでログインする end it 'ユーザーAが作成したタスクが表示される' do # 作成済のタスクの名称が画面上に表示されていることを確認 end end end end
FactoryBotを用いてデータを作る
FactoryBotを用いてデータを作る場合、buildまたはcreateというメソッドを使います。
before do # テストユーザーを作成(今回の場合は、user_aを作成) user_a = FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com') # 作成者がユーザーAであるタスクを作成しておく # userオプションを付けることで、taskファクトリーとuserの関連を指定している。=>user_aが作成したタスクになる FactoryBot.create(:task, :name: '最初のタスク', user: user_a) end
今回のようにユーザーAという特定のユーザーを作りたいので、nameカラムやemailカラムを直接指定して変更しました。もしオプションを指定しない場合、FactoryBotの定義内で、設定された値でデータが作成されます。
# FactoryBotの定義 FactoryBot.define do factory :user do name { 'テストユーザ' } email { 'test1@example.com' } password { 'password' } end end
また、タスクデータを作る際に、userオプションを指定しない場合は、新しいuserオブジェクトを合わせて作成します。つまり、毎回タスクオプジェクトを作る際に新たなuserオブジェクトも作成されます。
createとbuildの挙動の違い
createはオブジェクトを作成してデータベースに登録します。
buildはオブジェクトを作成するだけでデータベースに登録しません。
また、RSpecでのcreateはテスト終了時にDBが自動でロールバックされます。
これによりテストの度にFactoryBot.createを実行されても、また新たなデータとしてデータベースに登録できます。(ロールバックが行われないとDBエラーになります。)
createとbuildの使用用途の違い
DBの値を利用するテストが必要な場合にcreateを使います。
データの個数計算や一意のデータしか保存できないことなど、DBにアクセスして確認するテストにはcraeteを利用します。
DBにアクセスする必要がないテストの場合は、テストの実行速度を上げるためにbuildを利用します。
before(前提処理)にユーザーAとしてログインする処理を記載する
before do # ログインパスで、ログイン画面にアクセスする visit login_path # メールアドレスというラベル要素に入力値を入力する fill_in 'メールアドレス', with: 'a@example.com' # パスワードというラベル要素に入力値を入力する fill_in 'パスワード', with: 'password' # ログインボタンを押す click_button 'ログインする' ene
visit
特定のURLでアクセスする操作です。visit[URL] で実現できます。今回のログイン画面へのアクセスではこのように書きます。
visit login_path
fill_in
ラベルのついたテキストフィールドに値を入力する際に使用します。ラベルと入力値を指定して記述します。以下、メールアドレスでの例です。
fill_in 'メールアドレス', with: 'a@example.com'
click_button
ラベルのあるボタンを押します。以下、ログインボタンでの例です。
click_button 'ログイン'
it内の処理を記述する
it 'ユーザーAが作成したタスクが表示される' do # page(画面)に「最初のタスク」という内容があることを期待する # ここで、期待した処理がある時に、trueが返る expect(page).to have_content '最初のタスク' end
今までで説明したコードを応用すると、タスク一覧表示機能のSystem Specは以下のようになります。
require 'rails_helper' describe 'タスク管理機能', type: :system do describe '一覧表示機能' do before do user_a = FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com') FactoryBot.create(:task, name: '最初のタスク', user: user_a) end context 'ユーザーAがログインしているとき' do before do visit login_path fill_in 'メールアドレス', with: 'a@example.com' fill_in 'パスワード', with: 'password' click_button 'ログインする' end it 'ユーザーAが作成したタスクが表示される' do expect(page).to have_content '最初のタスク' end end end end
これでtasks_spec.rbのテストコードはひとまず完成しました。 以下のコマンドを実行することでテストが実行出来ます。
$ bundle exec rspec spec/system/tasks_spec.rb
$ bin/rspec 〜ようにbinでも実行可能です。
RSpecでの文法の捉え方
expect ~ to -
↑
「~に-を期待する」
「I expect that ~.」を言い換えた「~に-」を期待するという場合に使います。
最後に...
今回のテストコードは書籍の序盤の内容に沿って書いたテストのため、まだまだ修正出来る点があるかと思います。 良ければ次回以降の記事も参考にしてください。
参照
本書のみ
2021年9月30日 現場Rails Chapter5 Rspecの基本形
Spec(テスト)の書き方
Rspecでは、以下の基本フォーマットに従ってSpec(テスト)を書きます。
# テスト対象 describe '〜機能', type: :system do # 条件下で想定する動作 context '〇〇の場合' do before do # テストコード実行前に実行する処理(前提条件) end # テストケース it '仕様の内容(期待の概要)' do # 期待通り、対象が動作するとSpec成功となり、想定通り動作しない場合、予期せぬ例外としてerrorが出ます。 # また、例外ではなく想定と違う場合にFailureが結果として出ます。 end end end
Rspecの主要なメソッド
主要メソッドの詳細を、以下にまとめます。
describe
「何について仕様を記述しようとしているのか」を記述します。
System Specでは達成したい機能や処理の名前を記述するケースが多いです。
例)
掲示板検索機能、画像アップロード機能、etc
context
テストの内容を「状況・状態」のバリエーションごとに分類するために利用します。技術的にはdescribeとエイリアスとなっていて、テストケースを整理・分類するために用いられます。主に、describe内で使います。
例)
トップ画像を1枚選択してアップロード, トップ画像を2枚選択してアップロード
it
itはテストケースを表します。itには "期待する動作を説明する文字列" と、"期待する動作" を書きます。
例)
トップ画像が登録されること
本文で絞り込み検索ができること (この場合はcontextは省略)
トップページに〇〇ボタンが表示されている (この場合contextは省略)
it '〇〇する' do # 期待する動作 end
before
describeやcontext内にbeforeを記述すると、対応するdescribeやcontextの領域内のテストコードを実行する前に、beforeのブロック内に書かれたコードを実行してくれます。つまり、beforeはその領域全体の前提条件を実現するためのコードを記述する場所です。
本書ではまだ解説されていない内容にはなりますが、以下のように使われます。
# ユーザーログインを行いユーザー詳細ページにアクセスしておきたいケース before do login(user) visit user_path end
同じ条件下で複数のテストケースを実行したい場合、beforeに書くことでDRYにできます。
beforeとitの関係については、現場Rails P.196の説明を引用します。
beforeの処理は、itが実行されるたびに新たに実行されます。次のitが実行されるまでにデータベースの状態は元に戻される為、あるテストケースのせいで別のテストケースが影響を受けるということは基本的には起きないようになっています。
FactoryBotとは?
Railsではテストで利用するデータベースとその他の目的で使うデータベースとを切り離して管理しています。そのため、テストで用いるテストデータを個別で作る必要があるのです。 FactoryBotはテストデータを簡単に作成するためのGemです。FactoryBotを使って、テストデータを作成する流れは以下のようになります。
- FactoryBotでデータを作成するためのテンプレートを用意します。
- System Specの適切なbefore(事前準備)などで、FactoryBotのテンプレートを使って、テスト用データベースにテストデータを投入します。
- 次のテストケースに進む前にデータ状態を戻す処理は、System Specが適切に処理を行ってくれます。
FactoryBotでテストデータを作成出来るように準備する
- Userのファクトリを作成
# spec/factories/users.rb FactoryBot.define do factory :user do name { 'テストユーザ' } email { 'test1@example.com' } password { 'password' } end end
上記では、factoryというメソッドを利用して、Userクラスのファクトリ(テストデータ)を定義しています。
Userクラスでファクトリ名を異なる名前にする場合はfactoryメソッドに対して:classオプションを指定します。
factory :admin_user, class: User do ...(省略)
- Taskのファクトリを作成
# spec/factories/tasks.rb FactoryBot.define do factory :task do name { 'テストを書く' } description { 'RSpec & Capybara & FactoryBotを準備する' } # userは assosiation :userの省略形です。 user <200b> end end
先程の例でUserクラス側でファクトリ名を異なる名前にした場合は、下記のように記述します。
assosiasion :user, factory: :admin_user # Userクラスのadmin_userというファクトリを紐付けます。 # ここでもassosiationを省略してuser factory: :admin_user とも書けます。
traitとは?
traitとは、FactoryBotで複数のサンプルデータを作成する際に使われるテクニックです。Factoryが複数ある場合、traitを使用することで記述の重複を減らすことができ、可読性を高めたコードにすることができます。
FactoryBot.define do factory :モデル名 do trait :上記モデルを継承したインスタンスの属性値 do #モデルの状態の違いによって、作成できるデータの場合分けができる end trait :上記モデルを継承したインスタンスの属性値 do end end end
以下の例は、userモデルが一般の場合(traitを付けない時)と、:editorの場合と、:writerの場合によって作成されるデータを分けています。 実際に、テストデータを作成する時に、:editorや:writerなどの属性値を渡すことで、traitで設定した適切なデータを作成することが出来ます。
FactoryBot.define do factory :user do id { '1' } name { 'admin' } crypted_password { User.encrypt('password') } role { :admin } trait :editor do id { '2' } name { :editor } crypted_password { User.encrypt('password') } role { :editor } end trait :writer do id { '3' } name { :writer } crypted_password { User.encrypt('password') } role { :writer } end end end
# writer属性を設定したtraitのテストデータを作成 $ user1 = factory(:user, :writer)`
読み進めていく中でGemfileでgemのバージョンを指定する記号の意味について疑問に思ったので調べました
バージョンを上げるには3つのバージョンを使って表記します。
バージョンは、
x.y.z
の形式で表すことが出来ます。
X: メジャーバージョン
大規模な変更が起き、今までの過去のバージョンとの互換性が失われるときにメジャーバージョンの数字をあげます。Y: マイナーバージョン
過去のバージョンとの互換性は失われませんが、新規機能の開発や機能改善が行われたときにマイナーバージョンの数字をあげます。自分たちのプロジェクトの影響を最小限に抑えつつきちんとアップデートするのであればマイナーバージョンは上げましょう。Z: パッチバージョン
主にバグ修正のためのバージョン情報です。バグ修正が行われたときにパッチバージョンの数字をあげます。自分たちのプロジェクトで扱う際には必ずこのバージョンは上げたほうがいいです。
Xはメジャーバージョン yはマイナーバージョン zはパッチバージョン
# バージョンはBundlerにお任せ gem 'faker'
# バージョンを1.7.2に固定 gem 'faker', '1.7.2'
# 1.7.2以上(上に制限はなし) gem 'faker', '>= 1.7.2'
# 1.7.2かつ1.8未満 # バッチバージョンの変更はOK。ただし、マイナーバージョンとメジャーバージョンの変更は行わない gem 'faker', '~> 1.7.2'
# 1.7以上かつ2.0未満 # バッチバージョンとマイナーバージョンは変更OK。たが、メジャーバージョンは変更は行わない。 gem 'faker', '~> 1.7'
以上のように、バージョンを指定しないと、互換性が失われる場合があります。例えば、$ bundle update
を使う場合など。(メジャーバージョンが変わってしまうため)
互換性があるとは、古いバージョンから、新しいバージョンにアップデートしても大丈夫であるということです。マイナーバージョンを上げる時は互換性に影響がない場合が多いですが、メジャーバージョンを変えてしまうと互換性がなくなってしまうことが多いので、注意が必要です。
参照
「セマンティック バージョニング」を読んだのでバージョニングについてまとめた
RSpecの(describe/context/example/it)の使い分け