単体テストコード #3 (コントローラーのテスト)
テスト方針
modelのテスト: インスタンス生成 -> validチェック (データベースとのやりとりを確認)
controller: リクエストを送ってみて -> レスポンスをチェック (クライアントとのやりとりを確認)
Request Spec
RSpec内のコントローラー用のテストの手法
% rails g rspec:request tweets
spec/requests/tweets_spec.rb
生成
createメソッド (≒ build)
build: インスタンス生成のみ
create: 1回DBに保存。(からの毎回ロールバック。多用すると重くなるらしい。)
getメソッド
テスト内でリクエストを生成
(直後にbinding.pry
してみたが、すぐにレスポンスまで行われるっぽい)
request
get root_path binding.pry # requestでリクエストの中身を確認 [1] pry > request => <ActionDispatch::Request GET "http://www.example.com/" for 127.0.0.1>
response
# response でレスポンスを確認 [2] pry > response => @cache_control={:max_age=>"0", :private=>true, :must_revalidate=>true}, @committed=false, @cv=#<MonitorMixin::ConditionVariable:0x000000011587ae68 @cond=#<Thread::ConditionVariable:0x000000011587ae40>, @monitor=#<Monitor:0x000000011587b020>>, @header= {"X-Frame-Options"=>"SAMEORIGIN", ...(以下省略)
status
# response.status でステータスを確認。200が正常値。 [3] pry > response.status => 200 # 良く目にする「404ページが見つかりません」は、このHTTPステータスのこと(らしい)。
body
# response.body でHTML情報を(全部)確認 [4] pry > response.body => "<!DOCTYPE html>\n<html>\n <head>\n <title>Pictweet</title>\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n \n \n\n <link rel=\"stylesheet\" href=\"/assets/application-9b8e2e0d675fa110d5daab5bb6 ..."(以下省略)
show のルーティング
% reils routes
=>
tweet GET /tweets/:id(.:format) tweets#show
.../:id まで必要!
×
get tweet_path
○
get tweet_path(@tweet)
=>
FactoryBot.create(:tweet)
で保存した@tweet
を引数に指定することでidが取得される
英単語
CTO(なぜ今)
Chief Technical Officer: テクニカルな(技術)部門の責任者 -> 最高技術責任者
CEO(なぜ今)
Chief Exective Officer: 業務執行役員(エグゼクティブ)の中の責任者 -> 最高経営責任者
代表取締役(なぜ今)
会社法第349条で定められた会社の代表者。法的権限を有する。
(株式会社の代表)
第三百四十九条 取締役は、株式会社を代表する。ただし、他に代表取締役その他株式会社を代表する者を定めた場合は、この限りでない。
2 前項本文の取締役が二人以上ある場合には、取締役は、各自、株式会社を代表する。
3 株式会社(取締役会設置会社を除く。)は、定款、定款の定めに基づく取締役の互選又は株主総会の決議によって、取締役の中から代表取締役を定めることができる。
4 代表取締役は、株式会社の業務に関する一切の裁判上又は裁判外の行為をする権限を有する。
5 前項の権限に加えた制限は、善意の第三者に対抗することができない。
Request Spec まとめ
投稿されたデータがきちんと保存されていることを確認
-> 実際にデータをDBに保存して、HTMLにデータが出力されていることを確認- DBに保存 =
create
- HTMLへの出力確認 =
expect(response.body).to include(@tweet.text)
- DBに保存 =
投稿検索フォームの存在を確認 -> 投稿を検索するという文字列 が含まれることを確認。(それでいいの?)
DB設計:中間テーブル
中間テーブル
テーブルが「多対多」の場合は、カラム同士の関係性だけを搭載したテーブルを間に作る。
user_classesテーブル
id | user_id | class_id |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 3 |
4 | 2 | 1 |
through
「多対多」のアソシエーションで使う。
usersテーブル
class User < ApplicationRecord has_many :user_classes has_many :classes, through: :user_classes end
classesテーブル
class Class < ApplicationRecord has_many :user_classes has_many :users, through: :user_classes end
user_classesテーブル
class UserClass < ApplicationRecord belongs_to :user belongs_to :class end
ジェイウォーク(jaywalk)
中間テーブルを使わないよくない実装の形。アンチパターンの一つ。
一つのidカラムに、複数の情報を入力した状態とか。
それを解消するための中間テーブル。
- jaywalk: 〔交通規則を無視して〕道路を横断する、横断歩道のないところを横切る◆【語源】jay(不注意な人)から。
コントーラー生成時に不要ファイルが作られないように。
config/application.rb
module HogeApp class Application < Rails::Application config.generators do |g| g.stylesheets false g.javascripts false g.helper false g.test_framework false end end end
READ ME
アプリケーションの説明書(rails new
で自動作成)
マークダウンで書く
結合テスト#2
今日の学び
新単語は先に書き出すべし
テストは基本ミスってるところを教えてくれるから、それに従う。
必要とされるのは、知識より根気。。。
have_selector
セレクタの有無を確認
expect(page).to have_selector ".content_post[style='background-image: url(#{インスタンス変数とか});']" # !カッコではなくスペース!
(追記2023/9/2)
セレクタの指定
要素: a, p, img (ドットなし)
属性: .クラス名, .id名 (ドットあり)
have_link
a要素に対して、リンクの有無を確認
expect('要素').to have_link 'ボタンの文字列', href: 'リンク先のパス'
have_no_link
当てはまるボタンがないことを確認
expect('要素').to have_no_link 'ボタンの文字列', href: 'リンク先のパス'
all
findは要素が1個の時しか使えない。allならまとめて取得できる。
# 同名のクラス 全要素 all('クラス名') #◯番目のhogeクラス。 #一番上に表示されているのが[0]、二番目が[1]... all('hoge')[0]
have_field
form要素の有無を確認。
#id名で指定するときに'#'はつけない。 have_field('id名') #入力された内容まで確認するならwith have_field('id名', with: "hoge")
find_link().click
a要素をクリックする時に使う
find_link('リンクの文字列', href: 'URL').click
サポートモジュール
spec/support/hoge_support.rb
を作成して、その中にモジュールを定義
#モジュールを定義 module HogeSupport def hoge(user) ... end end
spec/rails_helper.rb
のコメントアウトを外す。RSpec.configureに読み込み設定を追記。
# コメントアウトを外す Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } RSpec.configure do |config| # 下記を追記 config.include HogeSupport ...
これでテストファイル内で、hoge(user)が使えるようになる。
(インスタンスメソッドの定義に似ている)
補足
chat GPT このコードの意味を以下に解説します:
いざ 結合テスト
System Spec
結合テストを記述する仕組み。
- Model Spec: モデルのテスト
- Request Spec: コントローラーのテスト (RSpec基本機能)
- System Spec: 結合テスト (CapybaraというGemを使う: Rails 標準搭載)
Capybara = 結合テスト用のGem
≪chat GPT先生≫
Capybaraは、Webアプリケーションのテストをシンプルで人間らしいスタイルで書くためのRuby用のライブラリです。
この名前は、テストにおいてブラウジングセッションを操作するときに、ユーザーがキャピバラ(南米に生息する巨大な水生哺乳動物)のように、ゆっくりとしたステップでウェブページを操作するような感覚を持ってテストを書けることを示しています。
Capybaraの名前は、テストコードを書くプロセスがゆっくりとした動きであること、そして開発者がウェブページを操作する際に"キャピバラのような"体験を得られることを示しています。この名前は、テストコードを書く際に自然な感覚を持ち、効果的な統合テストを行う手助けをすることを意味しています。
ほんまかいな
まずはファイル作成
% rails g rspec:system users
spec/system/users_spec.rb
が生成
example整理(急にむずない?)
基本的には、それぞれの場合を想定した時に、ユーザー操作に対応する挙動・表示を順番に確認する。
ユーザー新規登録ができるときのexample
-> トップページ -> 新規登録画面 -> 情報入力 -> ユーザーモデル +1カウント -> トップページへ ...ユーザー新規登録ができないときのexample
-> トップページ -> 新規登録画面 -> 情報入力 -> ユーザーモデル +1されない -> 再び新規登録画面 ...
visitメソッド
実際にそのページを訪れる。
visit root_path
page have_content
page: 可視部の情報を格納 have_content: 該当の文字列を含むかどうかを判断
expect(page).to have_content('hoge')
fill_in: 埋める
自動でフォームに入力してくれる
fill_in 'フォーム名', with: '文字列' # !括弧じゃなくて、スペース
フォーム名 = label要素
label要素
inputとセットで持ちられることが多い。 for属性 と id属性 によって紐付けを確認。
<div class="example"> <label for="hoge">Example</label> <input type="checkbox" id="hoge"> </div>
- 文字列 Example とチェックボックスを紐づける
- label要素ををクリックしてもフォームがアクティブ化する とかのメリットがある(らしい)。
上記の例では、Exampleがフォームの名
(for = id = "hoge" でinput要素と紐づいたlabel要素を確認。)
fill_in 'Example', with '文字列'
(追記 2023/9/2)
フォーム名として使えるのは
①for,idで紐づいたlabel要素のテキスト(上記)
②name属性
③普通にid属性
(クラス属性は×)
参考
find().click
findしてクリックしてくれる。 基本は要素を指定。たくさんある場合はnameで指定。
find('input[name="commit"]').click
change
ユーザーモデルのカウントが増えることを確認する時に使う。
expect{'動作'}.to change { Model.count }.by(1)
ブロックを渡すときは、{}
らしい。(なんのこっちゃ)
バックエンド側の処理とかを書くときは波括弧{}になるっぽい。
sleep 1
1秒待ってくれる。(←重要。つけ忘れたらできんかった。)
expect{ find('input[name="commit"]').click sleep 1 }.to change { User.count }.by(1)
current_path
今いるページがroot_path
かどうか確認
expect(page).to have_current_path(root_path)
トップページが開いたところでbinding.pry
してみた
[1] pry > current_path => "/"
これでもいいらしい
expect(current_path).to eq(root_path)
hover ( :空中に停止する。ホバリング)
マウスを合わせた時の挙動確認
expect( find('対象の要素').hover ).to have_content('文字列')
クラス要素を指定するときは'.hoge'
htmlの要素を指定するときは、'span'(ドットなし)だけど、クラス属性は、'.hoge'(ドットあり)。
あ、CSSと一緒か。なるほど。
have_no_content
文字列を含んでいないことを確認。
expect(page).to have_no_content('文字列')
英単語
directory(いまさら)
dis-(ばらばらに)+rego(導く)
->道順を案内する(direct)内容が記録された紙(-orium)、人を指揮する(direct)内容が記録された紙(-orium)
=> 住所氏名簿、ディレクトリ
- direct: ばらばらを(dis-)真っすぐに導く(rego) -> 指揮する、向ける、真っ直ぐな
- elect: ~の上へ(ex-)真っすぐに導く(rego)こと -> 立てる、直立する (-> ホモ・エレクトス:直立歩行する人)
- correct: con-(完全に、一緒に)+rego(真っすぐに導く、統治する) -> 訂正する、正す
- address: ~へ(ad-)真っすぐ(directus)に向けること -> 対処する、ヒトに宛てる、宛名
- alert: ex-(~の上へ)+rego(導く) -> イタリア語 all’(~へ)+erta(塔) -> 警戒した、警報
fill
いっぱいに(fullaz)すること -> 充満する、いっぱいに満たす。
- fill in: 埋める、書き込む
- full: 満ちた(fullaz) -> 満ちた、いっぱいの
- fulfill: いっぱい(full)に満たす(fill)こと -> 満たす、果たす、遂行する
element
elementum(世界を構成するとされた四元素のうちの一つ)が語源 -> 要素、元素
- elementum:(世界を構成するとされた四元素のうちの一つ)が語源。 -> 基礎に位置すること => 基本的な、要素の
- pixel: picture element(写真の要素) -> pixel
inspect
in-(~の中に)+specio(見る) -> 調査する、検査する
- expect: 外を(ex-)見る(specto)、外を見て待つ、期待する -> 期待する、予期する
- prospect: 前方を(pro-)見る(specio)こと -> 見通し、見込み
- aspect: ~へ(ad-)見たこと(spectus) -> 外見、様子、側面
- conspicious: よく(con-)見つけ(specio)た(-uus)、よく(com-)見る(specio)ことの多い(-ous) -> 目立つ、人目を引く
- despise: 見(specio)下す(de-)こと -> 見下す、軽蔑する
- respect: 後ろを(re-)見る(specio)、振り返ってみる、特別に見る -> 尊敬、特定の点
- perspective: 隅々を(per-)見(specio)ている(-ivus) -> 見方、遠近法、全体像
- suspect: こっそりと見(specio)上げる(sub-)こと -> 疑わしくおもう、容疑者
ambiguous(アンビギュアス)
動き(ago)回っ(ambo)ている(-uus)こと -> 曖昧な、多義な
- ambassador: hmbi-(あちこちに)+heg-(駆ける) -> 「お上のためにあちこち駆け回る人」 => 大使
- ambition: あちこち(ambi-)行く(eo)こと(-ion) -> 野心、大使
- transition: 別の場所へ(trans-)行く(eo)こと(-io) -> 遷移、推移
- initiate: ラテン語 in-(~の中に)+eo(行く) -> 始める(initio)こと => 始める、入会させる
いつもお世話になります
蛇足 Gemfileのグループ分け
Gemをインストールするときはどこに書くのが正解だっけ?ってなったので。
結論
開発環境でだけ使いたい
===> group :development do
テストでだけ使いたい
===> group :test do
開発環境とテスト環境で使いたい
===> group :development, :test do
本番環境でだけ使いたい
===> group :prduction do
全部の環境で使いたい
===> 上記のグループ以外の場所
参考
単体テストコード#2
メソッド
context: 条件ごとにグループ分け
使い方は、describe
と同じ。単なるグループ分け。見やすさ。
テストファイルを作成するときのコマンド
% rails g rspec:model tweet
spec/factories
の中と
spec/models
の中にファイルができる。
Faker::Lorem.sentence
- ランダムに文章を作成してくれる。
- 大文字注意
sentence
= 4単語、1文paragraph
= 3文、1段落
Lorem: 出版やグラフィックデザインなどに用いられるダミーテキスト「lorem ipsum」(ロレム・イプサム)の略。
Lorem ipsumの起源は古代ローマの哲学者、詩人キケロが書いた「善と悪の究極について」にさかのぼります。この著作の中で、キケロは「Neque porro quisquam est qui dolorem ipsum quia dolor sit amet・・・」と言う文章を使っています。
出典: Lorem ipsum(ロレム・イプサム)とは | 印刷・広告・デザイン用語集 | デザイン作成依頼はASOBOAD
≪chat GPT 日本語訳≫
「実際、どのような人間であっても、痛みそのもののために苦しむことはない。なぜなら、痛み自体が苦しみであるというわけではないからである。」
原文では、dolorem ipsum(痛み自体)だが、そこから意味のない言葉としてlorem ipsumが取り出されているらしい。(なぜに)
FactoryBot内のassociation
関連づけておくと、同時生成される。
FactoryBot.define do factory :tweet do association :user #userのインスタンスも同時生成 end end
英単語
context: 文脈、文中の言葉の前後関係、事情
context: 一緒に(con-)編んだ(texus)、構造、文の構造 -> 文脈、文中の言葉の前後関係、事情
単体テストコード
今日の工夫
カリキュラムは、INDEXのタイトル部分だけを先に読んでから、上から順に中身を読む。
全体像を把握した上で、個別の内容をinputする。
徒然草
世の中にはいろんな働き方があって、いろんな技術があって、いろんな言語があり、勉強しなければいけないことが山のようにある。
将来のこととか考えて悶々としてたが、ひとまずは今を楽しむことにする。
わかりやすい目標があって、サポートしてくれる人がいて、同じ課題に取り組む人がいる。
せっかく貴重な環境に身を置けているので、その時々を一生懸命に過ごさないと勿体無い。
メソッド・単語
Gem FactoyBot
(工場生産的な?)
ビルド(build)メソッド
FactoryBot
から新しいインスタンスを生成。
ActiveRecordで言うところのnew
#FactoryBot内で :user インスタンスを設定した上で FactoryBot.build(:user) #newと同じ意味 User.new(nickname: 'hoge', ...)
beforeメソッド
ActiveRecordで言うところのbefore_action
変数はインスタンス変数で。
before do @user = FactoryBot.build(:user) end
Gem 'Faker'
fake: 偽造する。ふりをする。偽物。
ランダムな値を生成してくれるGem。
文字数指定とか、大文字指定とかもできるっぽい。
大文字始まり(キャメルケース?)
Faker::Name.initials(number: 2) => "GR" Faker::Internet.email => "vida@oconnell.test" Faker::Internet.password(min_length: 6, max_length: 20) => "3OVpQDpPFe"
exampleの洗い出し
バリデーションとメソッドが対象
deviseによるバリデーション = validatable
- 以下が初期設定
- email {存在, 一意, @がある}
- password {存在, 6文字以上128文字以下}
正常系のテスト
be_valid
= 「valid?
->true
」を期待するmatcher
英単語
argument
明らかにする(arguo)こと(-mentum)、答えや主張を明らかにすること
-> 議論、議論の根拠、論証 引数
- argue: 明白にする(arguo)、答えや主張を明白にする -> を主張する、を議論する
まとめ
テストの流れは、
インスタンス設定
valid?
で保存できるか確認expect().to matcher
とにかく条件に合致する配列データ(インスタンス)をゴリゴリ作っていく感じ。
可能性を全部試すと言うより、条件を満たすサンプルを1個書ければそれでいい。