エラーメモ【TypeError】
エラーの解決過程を記録しておきます。
記事投稿アプリで記事内容を未入力で投稿しようとすると以下のようなエラーが発生しました。
TypeError - no implicit conversion of nil into String:
TypeErrorとは
メソッドの引数に期待される型ではないオブジェクトや、期待される振る舞いを持たないオブジェクトが渡された時に発生します。
エラー文を翻訳
no implicit conversion of nil into String
を翻訳すると
nilからStringへの暗黙の変換がない
となりました。
string型に変換する必要がありそうです。
該当箇所を確認
現在の該当箇所のコードは
def build_body(controller) result = '' article_blocks.each do |article_block| result << if article_block.sentence? sentence = article_block.blockable sentence.body end end result end
こんな感じです。
簡単に言うとコンテンツがあれば<<
でresultに追加する、というコードです。
コンテンツが存在すれば正常に処理され画面が表示されますが、コンテンツがないままだとエラーが発生する状態です。
string型のオブジェクトを取得したいのにnil型だから受け取れない、とのことなのでまずは実際に何型のオブジェクトか見てみます。
sentence.body.class
でsentence.body
がnil型だと分かりました。
これをstring型にできれば解決するはずです。
どうやって型を変えるか
初めに思いついたのはto_s
メソッドでstring型に変換するやり方です。
想定通り、string型になりました。
このやり方でもエラーは解決しましたが、||=
を使って空文字を明示する書き方の方が良いそうです。
def build_body(controller) result = '' article_blocks.each do |article_block| result << if article_block.sentence? sentence = article_block.blockable sentence.body ||= '' end end result end
このようにしてエラー解決できました。
||= は何をしているか
||=
は自己代入やnilガード
と呼ばれるそうです。
左辺がnilまたはfalseの場合は右辺が代入され、それ以外の場合は代入はされません。
sentence.bodyに「テスト」と入力して確認してみます。
入力されているのでnilにはならないはずです。
nil?メソッドでfalseが返ってきたのでsentence.bodyはnilではないことがわかります。
nilではないので右辺の''(空の文字列)は代入されず「テスト」となっていますね。
書籍などで||=この書き方を見たり、nilガードという言葉自体は見たことありましたが、実際に検証して理解が深まりました。
最後に
||= これだけ見ると記号か暗号のようですが、hoge = 1
のような変数に代入するコードに||
という関所があって、hogeが空っぽの時は1さんに「うむ、入れてやろう」、hogeに何か入ってる時は「今hogeは満員だ。帰れ」と言ってるイメージで覚えました。知らんけど。
今回は以上です。ありがとうございました。
参考サイト
class TypeError (Ruby 3.0.0 リファレンスマニュアル)
Rubyで使われる記号の意味(正規表現の複雑な記号は除く) (Ruby 3.0.0 リファレンスマニュアル)
【Ruby】使いこなせると便利。||演算子のいろんな使い方 - Qiita
Rubyの自己代入(||=)とはこんな仕組みです - Qiita
【Ruby入門】nilのポイントまとめ(nil? empty? blank? present?) | 侍エンジニアブログ
パンくずリストを作ろう
今回はパンくずリスト
を作ってみたいと思います。
パンくずリストとは
自分が今サイトのどこにいるかわかりやすく表示したものです。
こんなやつです。
アプリ立ち上げ
アプリの新規作成からやっていきます。
rails new gretel_app
名前はgretel_appとしておきます。
ターミナルでgretel_appに移動したら
rails g scaffold task title:string body:text
こんな感じでscaffoldして
rails db:migrate
マイグレートして
rails s
gem gretelの導入
gretel
というgemで簡単にパンくずリストを作ることができます。
Gemfileに
gem 'gretel'
として
bundle install
さらに
rails g gretel:install
とすると、config/breadcrumb.rb
が作成されます。
これで準備できました。
breadcrumb.rbに設定を記述
ではパンくずリストの設定をしていきます。
crumb :root do link "Home", root_path end # crumb :projects do # link "Projects", projects_path # end # crumb :project do |project| # link project.name, project_path(project) # parent :projects # end # crumb :project_issues do |project| # link "Issues", project_issues_path(project) # parent :project, project # end # crumb :issue do |issue| # link issue.title, issue_path(issue) # parent :project_issues, issue.project # end # If you want to split your breadcrumbs configuration over multiple files, you # can create a folder named `config/breadcrumbs` and put your configuration # files there. All *.rb files (e.g. `frontend.rb` or `products.rb`) in that # folder are loaded and reloaded automatically when you change them, just like # this file (`config/breadcrumbs.rb`).
こちらが先程のコマンドで作成されたファイルの中身です。
crumb :new_task do link 'New Task', new_task_path parent :root end
これでタスクの新規作成ページにパンくずリストを表示させる設定ができました。
crumb ページ名 do link ビューに表示したい名前, URL parent 親ページ名 end
省略しましたがroutes.rbでroot 'tasks#index'
としています。
viewファイルに記述
viewの表示させたい場所に記述していきます。
全てのページに表示させたい場合はapplication.html.erb
に
<!DOCTYPE html> <html> <head> <title>GretelApp</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <%= breadcrumbs separator: " › " %> <!--ここを追加 --> <%= yield %> </body> </html>
とします。
ページの上部にある方が分かりやすいのでyieldの上に置いてます。
separator: " › "
の部分は、親ページと子ページの間の文字を設定しています。
この場合、「>」になるんですが›
となっているのは、「>」はHTMLタグと認識されたりする場合があるためこのように特殊文字になっています。
他にもオプションがあるのでgretelのgithubのOptionsの項を参考にしてください。
次に各ページのファイルにも記述していきます。
新規作成ページ(new_task_path)を
<h1>New Task</h1> <% breadcrumb :new_task %> <!-- ここで呼び出し --> <%= render 'form', task: @task %> <%= link_to 'Back', tasks_path %>
このようにしました。
breadcrumb.rbで設定したページ名を指定することでパンくずリストを表示させます。
では確認してみましょう。
うまく表示できました。
次は詳細ページに表示させます。
breadcrumb.rbに
crumb :task_show do |task| link "About #{task.title}", task_path(task) parent :root end
詳細ページはIDが必要なのでブロック変数を使ってこのようにします。
viewには
<% breadcrumb :task_show, @task %>
このように記述します。
最後に編集ページです。
crumb :edit_task do |task| link 'Edit Task' parent :task_show, task end
編集ページは一番下の階層なのでlinkにはURLは必要ありません。
parentにもtaskを渡しておきます。
viewの方も
<% breadcrumb :edit_task, @task %>
として確認してみましょう。
詳細ページ
編集ページ
このようにこのように階層を重ねることもできます。
最後に
無事実装できました。
関係ないですがパンくずって何のことだろうとずっと思ってたんですが由来が面白いですね。
なぜhanselではなくgretelになったのかも気になるところです。
では今回は以上です。ありがとうございました。
参考サイト
GitHub - kzkn/gretel: Flexible Ruby on Rails breadcrumbs plugin.
【Rails】 gretelを使ってパンくずリストを作成しよう | Pikawaka - ピカ1わかりやすいプログラミング用語サイト
Rails パンくずリスト gretel の使い方 - Qiita
slimを使ってみよう
RailsではデフォルトのテンプレートエンジンはERBですが、他にもslim
やhaml
があります。
今回はslim
の記法について見て行きます。
テンプレートエンジンとは
HTML画面を直感的にわかりやすいテンプレート形式で記述することができます。
必要gemの導入
slimでhtmlを書くためのgemをインストールします。
・slim-rails
hoge.html.slimのようにビューファイルの拡張子にslimが使えるようになります。
・html2slim
既存のerbファイルをslim形式に変換してくれます。
Gemfileに
gem 'slim-rails' gem 'html2slim'
として
bundle install
これでgemがインストールできました。
サーバーは再起動しておきます。
erbをslimに変換
変換するためにターミナルでコマンドを入力します。
bundle exec erb2slim app/views
で元のerbファイルを残したまま変換されたhoge.html.slimファイルが作成されます。
元のerbを削除するには
bundle exec erb2slim app/views -d
とします。
slimの特徴
slimの特徴として
・<>、閉じタグがいらない
・<%= %> は =
・<% %> は -
・class指定 は .
・id指定 は #
・テキストは | の後に
・コメントアウトは /
が挙げられると思います。
scaffoldアプリを作ったのでどんな感じになっているか見てみましょう。
p#notice = notice h1 | Users table thead tr th | Name th | Email th[colspan="3"] tbody - @users.each do |user| tr td = user.name td = user.email td = link_to 'Show', user td = link_to 'Edit', edit_user_path(user) td = link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } br = link_to 'New User', new_user_path
まだ慣れていないので変な感じはしますが、コード量が減ってすっきりしていますね。
同名のerbファイルが残っているとerbの方が反映されるのでslimに移行するときはerbファイルは削除する必要があります。
最後に
慣れが必要ですが、覚えてしまえばerbより書くのも読むのも早くなりそうですね。
ただ、あえてslimからerbに戻した、という記事も見つけたので現場によってはデフォルトのerbのまま、ということもありそうです。
では今回は以上です。ありがとうございました。
参考サイト
【Ruby on Rails】テンプレートエンジンの種類(ERB,Haml,Slim) | プログラミングマガジン
【爆速で習得】Railsでslimを使う方法から基本文法まで - Qiita
【Rails】 slimの書き方をマスターしよう! | Pikawaka - ピカ1わかりやすいプログラミング用語サイト
SystemSpecを書いてみよう
前回の続きで今回はSystemSpec
を書いてみます。
SystemSpecとは
システムテストのことをRspecではシステムスペックと言います。
システムテストとは、実際に使用される状況と同じ設定でテストを行い、想定通りに動作するか検証することを言います。
テストの作成
spec/system/user_spec.rbを作成し、ここにユーザーに関するテストを書いていきます。
require 'rails_helper' RSpec.describe "Users", type: :system do end
大きく分けて
・ページ遷移
・ユーザー新規作成
・ユーザー編集
・ユーザー削除
の4つに関するテストを作ります。
describe
に対象、context
に条件、it
に具体的なテスト内容を記述します。
require 'rails_helper' RSpec.describe "Users", type: :system do describe 'ページ遷移確認' do end describe 'ユーザー新規作成' do end describe 'ユーザー編集' do end describe 'ユーザー削除' do end end
まずは対象で分けてみました。
次はdescribeの中にcontextで条件を書いていきます。
require 'rails_helper' RSpec.describe "Users", type: :system do describe 'ページ遷移確認' do context 'ユーザーの一覧ページに遷移' do end context 'ユーザーの新規作成ページに遷移' do end context 'ユーザーの編集ページに遷移' do end context 'ユーザーの詳細ページに遷移' do end end describe 'ユーザー新規作成' do context 'フォームの入力値が正常な場合' do end context '名前が未入力の場合' do end end describe 'ユーザー編集' do context 'フォームの入力値が正常な場合' do end context '名前が未入力の場合' do end end describe 'ユーザー削除' do end end
こんな感じになりました。
テストは正常系
と異常系
を作成します。
正常系は、全てのフォームが入力されている場合など、想定通りの操作をしたときの挙動を確認します。
異常系は、必須項目のフォームが空の場合など、想定外の操作をしたときの挙動を確認します。
なおユーザー削除は必ず成功する想定なので正常系のみです。
ページ遷移確認のテスト
RSpec.describe "Users", type: :system do let(:user) { create(:user) } describe 'ページ遷移確認' do context 'ユーザーの一覧ページに遷移' do it 'ユーザーの一覧ページへのアクセスに成功する' do user_list = create_list(:user, 3) visit users_path expect(page).to have_content 'ユーザー一覧' expect(page).to have_content user_list[0].name expect(page).to have_content user_list[1].name expect(page).to have_content user_list[2].name expect(current_path).to eq users_path end end context 'ユーザーの新規作成ページに遷移' do it 'ユーザーの新規作成ページへのアクセスに成功する' do visit new_user_path expect(page).to have_content 'ユーザー新規作成' expect(current_path).to eq new_user_path end end context 'ユーザーの編集ページに遷移' do it 'ユーザーの編集ページへのアクセスに成功する' do visit edit_user_path(user) expect(page).to have_content 'ユーザー編集' expect(page).to have_field 'Name', with: user.name expect(page).to have_field 'Gender', with: user.gender expect(current_path).to eq edit_user_path(user) end end context 'ユーザーの詳細ページに遷移' do it 'ユーザーの詳細ページへのアクセスに成功する' do visit user_path(user) expect(page).to have_content 'ユーザー詳細' expect(page).to have_content user.name expect(page).to have_content user.gender expect(current_path).to eq user_path(user) end end end end
例として2つ目のテストを挙げると
・visit new_user_path
ユーザー新規作成ページを訪れ
・expect(page).to have_content 'ユーザー新規作成'
そのページに「ユーザー新規作成」と表示されていて
・expect(current_path).to eq new_user_path
今いるpathがnew_user_path
ならテストが通ります。
let(:user) { create(:user) }
は、itの中でその都度user = create(:user)
でuserを作成するのではなく、予め定義だけしておいてuser
で呼び出せるようにしています。
残りのテスト
全部説明すると長くなってしまうので残り一気にいっちゃいます。
最終的なコードがこちら。
require 'rails_helper' RSpec.describe "Users", type: :system do let(:user) { create(:user) } describe 'ページ遷移確認' do context 'ユーザーの一覧ページに遷移' do it 'ユーザーの一覧ページへのアクセスに成功する' do user_list = create_list(:user, 3) visit users_path expect(page).to have_content 'ユーザー一覧' expect(page).to have_content user_list[0].name expect(page).to have_content user_list[1].name expect(page).to have_content user_list[2].name expect(current_path).to eq users_path end end context 'ユーザーの新規作成ページに遷移' do it 'ユーザーの新規作成ページへのアクセスに成功する' do visit new_user_path expect(page).to have_content 'ユーザー新規作成' expect(current_path).to eq new_user_path end end context 'ユーザーの編集ページに遷移' do it 'ユーザーの編集ページへのアクセスに成功する' do visit edit_user_path(user) expect(page).to have_content 'ユーザー編集' expect(page).to have_field 'Name', with: user.name expect(page).to have_field 'Gender', with: user.gender expect(current_path).to eq edit_user_path(user) end end context 'ユーザーの詳細ページに遷移' do it 'ユーザーの詳細ページへのアクセスに成功する' do visit user_path(user) expect(page).to have_content 'ユーザー詳細' expect(page).to have_content user.name expect(page).to have_content user.gender expect(current_path).to eq user_path(user) end end end describe 'ユーザー新規作成' do before { visit new_user_path } context 'フォームの入力値が正常な場合' do it 'ユーザーの新規作成が成功する' do fill_in 'Name', with: 'test_name' select '男性', from: 'Gender' click_button '登録する' expect(page).to have_content 'User was successfully created.' expect(current_path).to eq '/users/1' end end context '名前が未入力の場合' do it 'ユーザーの新規作成が失敗する' do fill_in 'Name', with: nil select '男性', from: 'Gender' click_button '登録する' expect(page).to have_content '1 error prohibited this user from being saved:' expect(page).to have_content 'Nameを入力してください' expect(current_path).to eq users_path end end end describe 'ユーザー編集' do before { visit edit_user_path(user) } context 'フォームの入力値が正常な場合' do it 'ユーザーの編集が成功する' do fill_in 'Name', with: 'update_name' select '男性', from: 'Gender' click_button '更新する' expect(page).to have_content 'User was successfully updated.' expect(current_path).to eq '/users/1' end end context '名前が未入力の場合' do it 'ユーザーの編集が失敗する' do fill_in 'Name', with: nil select '男性', from: 'Gender' click_button '更新する' expect(page).to have_content '1 error prohibited this user from being saved:' expect(page).to have_content 'Nameを入力してください' expect(current_path).to eq user_path(user) end end end describe 'ユーザー削除' do let!(:user) { create(:user) } it 'ユーザーの削除が成功する' do visit users_path click_link 'Destroy' expect(page.accept_confirm).to eq 'Are you sure?' expect(page).to have_content 'User was successfully destroyed.' expect(current_path).to eq users_path end end end
・ユーザー新規作成のbefore { visit new_user_path }
describe 'ユーザー新規作成'
内の2つのcontextは両方ともまずnew_user_pathにアクセスするところから始まります。それぞれのテストにいちいち書くのはDRYに反しますね。なのでbeforeでまとめてしまいましょう。ユーザー編集のbeforeも同様です。
・ユーザー削除のlet!(:user) { create(:user) }
先ほどのletと違い、!がついています。
2つの違いはuserが作成されるタイミングです。
・letはuserが呼び出されたときに作成され、
・let!はテスト実行前に作成されます。
はい、よくわかりません。
具体的に見てみます。ユーザー編集の正常系のテストはこのようになっています。
RSpec.describe "Users", type: :system do let(:user) { create(:user) } 略 describe 'ユーザー編集' do before { visit edit_user_path(user) } context 'フォームの入力値が正常な場合' do it 'ユーザーの編集が成功する' do fill_in 'Name', with: 'update_name' select '男性', from: 'Gender' click_button '更新する' expect(page).to have_content 'User was successfully updated.' expect(current_path).to eq '/users/1' end end
該当のユーザーの編集ページを訪れ
Nameに「update_name」と入力し
Genderは「男性」を選択
「更新する」ボタンをクリックすると
ページに「User was successfully updated.」と表示されていて
そのページのpathは「/users/1」になっている
とテストが成功する。
という内容です。
この最初のvisit edit_user_path(user)、ここでuser
が出てきてます。このタイミングで実際にcreate(:user)されています。
次にユーザー削除のテストを見てみましょう。
describe 'ユーザー削除' do let!(:user) { create(:user) } it 'ユーザーの削除が成功する' do visit users_path click_link 'Destroy' expect(page.accept_confirm).to eq 'Are you sure?' expect(page).to have_content 'User was successfully destroyed.' expect(current_path).to eq users_path end end
こちらはlet!(:user) { create(:user) }
としてあります。
このテストは
users_path(ユーザー一覧ページ)を訪れ
「Destroy」というリンクをクリック
アラートで「Are you sure?」と表示され
OKすると「User was successfully destroyed.」と表示され
users_pathに遷移される
とテスト成功です。
が、itの中を見てみるとuser
はどこにも呼び出されていません。
しかしそもそもユーザーが1件もないとDestroyというリンクは表示されないような実装にしているので、itの中でuserを作成する必要があります。
let(:user) { createa(:user) }
と定義しているだけではuserは作成されないので、let!(:user) { create(:user) }
でテスト実行前に予め作成しておかないといけません。
ユーザー削除のテストの結果をletとlet!で見比べてみます。
letの場合
let!だと想定通り、テストが通りますが、
letの場合
「Destroy」なんて見つからない、と言われてしまいます。
前述の通り、ユーザーが0件だと「Destroy」が表示されない、つまりユーザーが作成されていない。
削除のテストを実行するためにはやはりletではなくlet!でユーザーの作成が必要だとわかります。
最後に
簡単なCRUDアプリなのでシンプルなテストになりました。もっと多機能なアプリだとテストも複雑になりそうです。あとテストではハードコーディングしたほうがいいのかなー、と思ったんですがどうなんでしょう。テスト件数が増えるとメンテナンスが大変になりそうですがそっちの方が堅牢なテストになりそうな気もします。
今回も自分にとってしっくり来る表現をしたので厳密には間違ってたりするかもしれません。ご指摘あればコメントいただけると嬉しいです。
長くなりましたが、読んでいただきありがとうございました。
参考サイト
使えるRSpec入門・その4「どんなブラウザ操作も自由自在!逆引きCapybara大辞典」 - Qiita
Module: Capybara::Node::Actions — Documentation for jnicklas/capybara (master)
システムテストとは?開発段階のテストの流れと主な種類|発注成功のための知識が身に付く【発注ラウンジ】<
RSpecの(describe/context/example/it)の使い分け - Qiita
【直感的に書ける!】RspecでRubyのテストを覚えよう! | プロぽこ
RSpec の letとlet!とbeforeの挙動と実行される順番 - Qiita
Rspecを書いてみよう
初めてテストコードを書いたのでおさらいのためにまとめてみます。
RailsはMInitestというテスティングフレームワークを備えていますが今回は
以前作った簡単なCRUDアプリ
を元にRspec
を導入してテストを書いてみました。
テストが必要なほどの機能はないのですが練習のため。
2つの違いはよくわかってませんがRspecの方が利用率は高いそうです。
ではまずは導入からやっていきます。
必要gemのインストール
必要なgemを4つインストールします。
group :development, :test do gem 'rspec-rails' gem 'factory_bot_rails' end group :test do gem 'capybara' gem 'webdrivers' end
rspec-rails
テスティングフレームワーク。
これがないと始まらない。
GitHub - rspec/rspec-rails: RSpec for Rails 5+
factory_bot_rails
テスト用のデータを簡単に作成できるgem。
GitHub - thoughtbot/factory_bot_rails: Factory Bot ♥ Rails
capybara
E2Eテスティングフレームワーク。
ブラウザでテストしてくれる。
GitHub - teamcapybara/capybara: Acceptance test framework for web applications
webdrivers
調べたんですがあんまりわかってません。
capybaraとセットで使われるっぽい。
selenium-webdriver
、chromedriver-helper
の代わりにこのwebdrivers
1つでいいみたいです。
GitHub - titusfortner/webdrivers: Keep your Selenium WebDrivers updated automatically
bundle install
でインストールします。
諸々の設定
rails g rspec:install
とすると
Running via Spring preloader in process 63316 create .rspec create spec create spec/spec_helper.rb create spec/rails_helper.rb
これらが作成されます。
.rspec
に--format documentation
を追加するとテスト結果の表示が変わります。
お好みで設定しましょう。
rails_helper.rbの
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
これのコメントアウトを外すとspec/support以下のファイルを読み込みます。
モジュールなどを作成する場合はsupportディレクトリを作成し、その中に置きます。
このアプリにログイン機能はまだないので以下は例です。
# support/system_helper.rb module LoginModule def login(user) visit '/login' fill_in 'Name', with: user.name fill_in 'Email', with: user.email fill_in 'Password', with: '00000000' click_button 'Login' end end
support/capybara.rbを作成して
RSpec.configure do |config| config.before(:each, type: :system) do driven_by :selenium_chrome_headless end end
ドライバーの設定もしておきます。
selenium_chrome_headless
を指定しておくとテスト実行の度にブラウザを立ち上げずにテストしてくれます。
テスト用データの作成
rails g rspec:model user
このコマンドでmodels/user_spec.rbとfactories/user.rbが作成されます。
FactoryBot.define do factory :user do end end
こちらがfactories/user.rbの中身です。
ここでテスト用のデータを作成します。
userモデルとスキーマのusersテーブルは
class User < ApplicationRecord validates :name, presence: true enum gender: { male: 0, female: 1, secret: 2 } end
create_table "users", force: :cascade do |t| t.string "name", null: false t.integer "gender", default: 0, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end
こんな感じです。
FactoryBot.define do factory :user do name { 'test_name' } gender { 'male' } end end
名前と性別を設定しておきました。
テストで使うデータの初期値を設定しておく、というイメージです。
また、rails_helper.rbに
RSpec.configure do |config| config.include FactoryBot::Syntax::Methods end
これを追加しておくと、テストでデータを作るときに
# 設定なし user = FactoryBot.build(:user) # 設定あり user = build(:user)
このように省略できるので設定しておきます。
テストの作成
ようやくテストの作成です。
今回はバリデーションに関するテストを作ってみます。
先ほど作成したmodels/user_spec.rbに
require 'rails_helper' RSpec.describe User, type: :model do describe 'バリデーション' do it '全てのフォームが正常な場合に成功する' do user = build(:user) expect(user).to be_valid expect(user.errors).to be_empty end it 'Nameが空の場合に失敗する' do user = build(:user, name: nil) expect(user).to be_invalid expect(user.errors[:name]).to eq ['を入力してください'] end end end
2つのテストを書いてみました。
1つ目のテストを説明すると、
・user = build(:user)
factories/user.rbの情報を元にユーザーを作成しuserに代入
・expect(user).to be_valid
userがバリデーションを通ることを期待
・expect(user.errors).to be_empty
user.errorsが空であることを期待
となります。
2つ目だと
・user = build(:user, name: nil)
初期値を元にするがnameは空にする
・expect(user).to be_invalid
userがバリデーションに引っかかることを期待
・expect(user.errors[:name]).to eq ['を入力してください']
エラー文、「を入力してください」と表示されることを期待
となります。
それではテストを実行してみましょう。
bundle exec rspec
とすると
通りました。
最後に
ほんとに簡単なところだけですがテストを作って実行するところまでできました。実装のコードは間違っていたらエラーが出ますが、テストはそもそもテストコードをちゃんと書かないと意味を成さない気がするので気を付けたいです。何か間違いなどありましたらコメントいただけると嬉しいです。
では今回もありがとうございました。
参考サイト
E2Eテスト
RSpecとminitestのおおまかな違い - Qiita
FactoryBotでテストデータ作成する方法 - Qiita
【Rails】『RSpec + FactoryBot + Capybara + Webdrivers』の導入&初期設定からテストの書き方まで | vdeep
git, github超基礎
git、githubについてあまり分かってないまま学習してきたので、かなり初歩的な内容になると思いますがまとめてみます。
gitとは
gitとは分散型バージョン管理システム
のことです。
分散型と集中型がありますが集中型についてはここでは扱いません。
バージョン管理とはいつ、誰が、どんな編集をしたか、その履歴を管理することです。
履歴ごとにセーブポイントを作って、どの地点にも戻ることができます。
githubとは
どのように管理するか
gitで作った履歴をgithubでオンラインで管理する、ということがわかりました。
では、もう少し具体的に見ていきたいと思います。
リポジトリ
ファイルの保管場所のことをリポジトリ
と言います。
プロジェクト単位でリポジトリを作ることが一般的なようです。
リポジトリは
に分かれます。
gitが用意してくれているサーバーに対してファイルをアップロード、ダウンロードすることでファイルの共有ができますが、このインターネット上に保存したリポジトリをリモートリポジトリ
と言います。
githubアカウントでリモートリポジトリを作成し、自分のPCにコピーしたものがローカルリポジトリ
です。
クローン
リモートからローカルにリポジトリをコピーすることをクローン
と言います。
git clone [URL]
でクローンできます。
コミット
クローンしたファイルで開発を進め、きりのいいところでセーブします。
このセーブのことをコミット
と言います。
git status
というコマンドで変更したファイルの一覧が表示されるので、
git add [ファイル名]
で指定したファイルがgitの管理下に置かれます。この状態をステージング
と言います。
ステージング状態ではまだセーブされていません。セーブするには
git commit -m 'コミットメッセージ '
とします。
コミットにはどんな変更を行ったかを端的に表すコミットメッセージを付けておきます。
これがないと後から戻ろうとしてもどのセーブポイントに戻ればいいのか分からなくなります。
また、コミット履歴を確認するには
git log
コマンドを実行します。
プッシュ
ローカルの変更箇所をセーブできましたが、この時点ではまだリモートリポジトリには反映されていません。
リモートに反映させることをプッシュ
と言います。
git push origin
origin
とはリモートリポジトリのことです。
このコマンドでリモートにも自分がコミットした内容が反映されます。
プル
最新のリモートリポジトリの情報をダウンロードすることをプル
と言います。
git pull origin
このコマンドで、他の開発者がプッシュした内容をプルしてローカルに持ってきます。
ブランチ
ブランチ
とは作業場所のようなものです。
gitの変更履歴を枝分かれさせ、新しい作業場所を作ることをブランチを切る、と言います。
元となる作業場所はマスター
と言います。
masterーーcommitーーcommitーーcommitーーcommitーーcommitーー
branch └ーーcommitーーcommit--commitーー
git checkout -b ブランチ名 master
checkout
自体はブランチを移動するコマンドです。
-b
と付けることで新しいブランチを作り、ブランチ名 master
でmasterから新しいブランチに移動できます。
現在のブランチを確認するには
git branch
を実行します。
プルリクエスト
ブランチを切って開発を進め、コミット、プッシュまでしたらgithubでプルリクエスト
を行います。
プルリクエストとは、他の開発者にレビューしてもらい、変更内容がOKならマージしてください、という依頼です。
マージ
ブランチをマスターに合流させることをマージ
と言います。
マスターブランチにマージする際はマスターに移動して
git marge 合流させるブランチ名
とします。
マージするまではブランチのコミットはマスターには反映されません。
実務では初めのうちはマージすることはなさそうな気がしますがどうなんでしょう。
僕はまだ学習中なのでわかりませんが、マージ怖い、という印象です。
大まかな流れはこんな感じかな、と思います。
最後に
厳密には違う表現もあるかも知れませんが、大体のイメージは掴めるんじゃないかなと思います。
必須知識らしいのでちゃんと身につけたいですね。
では今回は以上です。ありがとうございました。
参考サイト
【Git入門】Git + Github使い方入門講座🐒Gitの仕組みや使い方を完全解説!パーフェクトGit入門! - YouTube
GitHubとは?覚えておくべき7つの基礎知識や使い方を解説 – IT業界、エンジニア、就活生、第二新卒、転職者、20代向け情報サイト
JavaScript DOM操作練習してみた
今回はJavaScriptのDOM操作を練習してみます。
DOMとは
DOM (Document Object Model) とは、すべての HTML または XML 文書を表現・操作する API です。 DOM はブラウザーで文書構造をノードのツリーとして読み込み、それぞれのノードを文書の一部 (例えば要素、テキスト文字列、コメント) として表します。引用元
この仕組みを使ってJavaScriptを使ってHTMLの要素を取得、操作します。
実践
早速やってみます。
まず適当にファイルを作りHTMLを書いていきます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Practice</title> </head> <body> <p id="text">ここが変わります</p> <button id="button">クリック</button> </body> </html>
ブラウザで開いてみるとこんな感じです。
こうなりました。
まだhtmlしか書いてないので当然クリックしても何も起きません。
ここから、ボタンをクリックしたら「ここが変わります」の部分が変わるようにしていきます。
とりあえずJavaScriptのコードを書き込む場所を作ります。
<body> <p id="text">ここが変わります</p> <button id="button">クリック</button> <script> </script> </body>
ちょっと練習する用なのでbodyタグの一番下にscriptタグを書いて直接htmlファイルに書きます。
script内をこのようにします。
<script> document.addEventListener('DOMContentLoaded', () => { const text = document.getElementById("text"); const button = document.getElementById("button"); button.addEventListener('click', () => { text.innerText = ('変わった?'); }); }); </script>
すると
変わりましたね。
ではscriptタグの中を見ていきましょう。
document
には現在表示しているページのすべてのHTMLやCSS、JavaScriptなどの文章が入っています。
試しにscript内にconsole.log(document);
を入れてみると
コンソールにファイルの内容が表示されました。
addEventListener
はイベントを実行するメソッドです。
引数のDOMContentLoaded
イベントは HTML ページの読み込みが完了しHTML のパース(解析)が完了してDOM ツリーの構築が完了した時点で発生するイベントです。
正直、まだあまり理解できていないのでおまじないだと思ってます。
さらに中を見ていきます。
getElementById
でhtml要素を取得します。
何を頼りに要素を探すかというと、その名の通りId
です。
そのためhtml要素にはidを設定している必要があります。
const text = document.getElementById("text");
で、textというidが付いている要素を取得してきて、textという定数に代入しています。
const button = document.getElementById("button");
も同様です。
button.addEventListener('click', () => {
定数buttonをクリックしたときにイベントが起きます。
text.innerHTML = ('変わった?');
定数textを`innerHTMLで要素を置き換えています。
流れとしてはこんな感じです。
他にもドットインストールで簡単なゲームを作ったりすればどんなことができるかわかりやすいと思います。
最後に
初めは何をやってるのかさっぱりでしたがなんとか動きました。基本は取得→操作の流れなのでしっかり考えれば色々遊べそうですね。
では今回は以上です。ありがとうございました。
参考サイト
【JavaScript】DOMを理解してHTMLを操作してみよう | RAKUMAオンラインスクール
JavaScript入門 | Let'sプログラミング
【JavaScript入門】addEventListener()によるイベント処理の使い方! | 侍エンジニアブログ
MDN Web Docs
はじめてのJavaScript (全11回) - プログラミングならドットインストール