# 【第14週】パRails輪読会 \(2022\-10\-24\~ 2022\-10\-28\)
###### tags: `パRails(2回目)`
- [開催概要](https://hackmd.io/rOcLR0riRqmOgEF0_Ssm0A?both)
- [パRails輪読会 ノートまとめ](https://hackmd.io/5emISRvRRXapmakSiHnFJg?both)
## パRailsのサンプルコード・正誤表
- [サポートページ:パーフェクトRuby on Rails【増補改訂版】:|技術評論社](https://gihyo.jp/book/2020/978-4-297-11462-6/support)
- [パRails 環境構築の手順](https://hackmd.io/y7qb2BRMT2Wd4tAtKYObcQ)
## 目次
[TOC]
------
## 2022\-10\-24(月)
### 連絡事項や確認・相談
### タイムキーパー
- Saki
### ドライバー
- tomonariさん
### 読んだところ
- P.361[7-4-6 ログインが必要なページのテストを書く]〜
### 次回
- P.361[7-5 コントローラに対する機能テスト]〜
### 自由に使う共有スペース
### 各自の疑問点や気づき、学んだこと
- maimu_x2x
- assert_diffrenceを使うと数の増減をテストできる
- travel_toにブロックを渡すと、ブロックの中では引数に渡した時刻を現在時刻と差し替えられる
- Railsのテストの課題で使ったのですが、使い方が間違ったまま提出して撃沈しましたw
- hikaru
- ログインヘルパー便利
- setupDRY
- `start_at_field = "event_start_at"`は入力欄を選んでいる?
- labelの値をみているらしい
- `assert_difference`はいろんな場所で見る。便利
- `travel_to`のブロック内で時間旅行ができる
- @garammasala29
- `travel_to`メソッドはタイムトラベルができて便利そう。日時関連のテストを書く時に思い出したい
- テストはどこまでDRYにすべきか悩みます。
- webdriversのバージョンを上げないとエラーが出るやつ、ブログ記事にしようとしたら伊藤さんが既にQiitaに上げていて、さすがだな〜と脱帽
- Tokyu.rb、疑ってしまって申し訳ありません
- Saki
- webdriverのエラーが起こったら、バージョンを指定してbundleする。`gem 'webdrivers', '>= 5.2.0'`
- [\[🐛 Bug\]: chromedriver for Apple M1 has changed name · Issue \#11066 · SeleniumHQ/selenium](https://github.com/SeleniumHQ/selenium/issues/11066)
私もM1なのに手元で同じエラーが起こらなかったのはなぜだろう
- `assert_diffrence`は、do~endの間に書いた処理の前後で、モデルのレコードの増減をアサーションできる。公式:https://api.rubyonrails.org/v7.0.3/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_difference
- テストは全部DRYにすることが正解でなく、DRYにしすぎると読みにくい
- @tomonari
- 入力フォームのロールボックスの数値指定が結構大変そう。
- set_upでログイン処理とかを書いてDRYにしたくなりますが、呼ぶ必要がないメソッドがあるときは書かないほうがいいんですね。
- @haruguchi
- https://api.rubyonrails.org/v7.0.3/classes/ActiveSupport/Testing/TimeHelpers.html
- https://api.rubyonrails.org/v7.0.3/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_difference
### 本日の振り返り(よかった点・次回に向けての改善点・今の気分などなんでもOK)
- maimu_x2x
- Discordの輪読会チャンネルが折りたたまれていて、最初テキストチャンネルしか見れず、数分思考停止しましたw
- mac買い換えようと思っているのですが、皆さんはMacBookAirですか?
- Proが欲しかったのですが高すぎて無理・・・となりました。
- Airです!去年の12月にPro買おうと思っていたら売り切れだったので:cry:
- チーム開発で重すぎてデモのとき15分くらいかかったのでM1 Mac Pro買いました!
- proですが、ディスク128GBしか買えませんでした...Docker入れたらアップデートできなくなりました...
- @garammasala29
- 最高気温が今日は2桁いきませんでした
- Saki
- 金曜に私用で輪読会参加できないのですが、私だけ休んで普通に開催してもらうか、お休みにするか悩んでます〜どっちの方が皆さんやりやすいですかね🤔☁️
- @tomonari
- KaigionRailsでライブコーディングしてましたが、ああいう風に素早くコーディングできるようになりたいです。
- hikaru
- マイクの設定が上手いこといきません:cry:
- ずっと色ついててもそんなにノイズは聞こえてないですよ!
- つま先が冷えてる
- @haruguchi
- そろそろ鍋の用意をします!
- そろそろ
- 気になる方はロール登録しましょう!! https://discord.com/channels/715806612824260640/874564711700656179/958982827066675231
------
## 2022\-10\-25(火)
### 連絡事項や確認・相談
### タイムキーパー
- Saki
### ドライバー
- Hikaruさん
### 読んだところ
- P.361 [7-5 コントローラに対する機能テスト]〜
### 次回
- P.373 [7-6 モデルに対するテスト]〜
### 自由に使う共有スペース
```ruby!
def sign_in_as(user)
...
case
when self.respond_to?(:visit) # selfを補った
visit root_url
click_on 'GitHubでログイン'
when self.respond_to?(:get)
get '/auth/github/callback'
else
raise NotImpletedError.new
end
...
end
# メソッド呼び出す時
self.sign_in_as user #selfを補った ここのレシーバがrespond_to(:foo)に応答するか上のメソッド定義で見ている
```
### 各自の疑問点や気づき、学んだこと
- @maimu_x2x
- ログインヘルパーでcase文を使ってシステムテストと機能テストで使い分けができるの便利。
- 動作なのか処理なのか何を確認するかで書くテストは異なる。
- @hikaru
- 機能テストはシステムテストで都合が悪いこともテストできる
- インテグレーションテストは複数のリクエストにまたがるテストを書くためのもの
- LEF
- Rubyのオブジェクト指向で`self`が省略されているのに気づかなくて(意識し忘れていて)、「この挙動どうなっているんだっけ?」となってしまったことがたびたびあったので、classがある場所での`self`の省略については意識していきたいです。
- @fuwa
- 機能テストはシステムテストよりも高速に実行できるのがメリット。良い感じに使い分けていきたい
- Saki
- 「イベント作った人しかイベント削除できない」テストはシステムテストで書けばいいのでは?の答え
- システムテストはユーザーが行なった操作に対する挙動をテストする
- コントローラテストは、ユーザーが行えない、内部ではしってほしい処理や実行結果をテストする。なのでリスト7.23では`assert_raises(ActiveRecord::RecordNotFound)`と書いてある。システムテストは、ユーザーの操作ではこのエラーは起こせず内部の処理をテストできない。
- コントローラのテストはAPIをテストするイメージだったので、↑なるほどでした!
- @tomonari
- 権限周りは想定通りの動きをしないと大変なことになるのでテストが必要とのことなので、ここはしっかりテストを書いておきたいと思いました。
- @haruguchi(特に読まなくて良いです)
- 上にrespond_to?のレシーバを補ったコードを書いておきました。
### 本日の振り返り(よかった点・次回に向けての改善点・今の気分などなんでもOK)
- LEF
- 新しいパソコンがやっと届きました。まだ開封していないですが、セットアップのことを考えるとちょっと面倒……カモ?🦆
- Mac miniも持っていて、WSL2よりそっちのほうが色々楽そうですが、いったんWSL2で始めてしまったので、最後までWSL2で頑張りたい気持ち……
- @maimu_x2x
- 寒すぎます・・・ウールコートはまだ早いかな?
- atcoderの問題を初めてみたのですが、設問の日本語が難しいw
- 慣れかもです!
- @fuwa
- 今日はあんまり集中できない一日だったので明日がんばりたい :wakaru:
- なんでdiscordの画面共有の解像度が悪いんだろう。。MacBookProでメモリ32GBもあるのに
- intel chip or apple chip?
- fast.com (by Netflix) はどうですか -> 問題なさそう
- https://fast.com/ja/
- Saki
- もう少しで自作サービス提出できそうあのですが、スマホのデザインが鬼難しくて町田さんにペアプロしていただくことになりました😂
- この後お時間ある方私のアプリでログインできるか動作確認してもらえると嬉しいです! https://fjord-choice.herokuapp.com/
- @haruguchi
- 半袖で過ごしてしまった、、、
- ネットワークについて理解したい、、、オススメの本求む
- https://www.amazon.co.jp/%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0TCP-IP%E2%80%95%E5%85%A5%E9%96%80%E7%B7%A8%E2%80%95-%E7%AC%AC6%E7%89%88-%E4%BA%95%E4%B8%8A-%E7%9B%B4%E4%B9%9F/dp/4274224473

- hikaru
- コピペが楽だったので電子書籍の人はぜひ...ちょっとコードが長くなると選択が難しくなるのが😵
- :naruhodo:
- @tomonari
- 未だにRailsのmodelから他のmodelにデータを渡すのどうやるんだろう?となります。
------
## 2022\-10\-26(水)
### 連絡事項や確認・相談
### タイムキーパー
- Hikaruさん
### ドライバー
- tomonariさん
### 読んだところ
- P.373 [7-6 モデルに対するテスト]〜
### 次回
- P.378 [COLUMN バリデーションのテストを簡潔に書く]
### 自由に使う共有スペース
- Hikaruさんが共有くださったコード
https://github.com/skmetz/poodr2
```ruby=
# スタブの例
# メソッドを呼んだ返り値を使って処理ができるかテストする例
class Wheel
attr_reader :rim, :tire
def initialize(rim, tire)
@rim = rim
@tire = tire
end
def width # Wheelクラスにwidthが実装されている
rim + (tire * 2)
end
end
class DiameterDouble # Wheelクラスの役割(=widthを返す)を置き換えたもの、double=代役
def width # Wheelクラスのwidthを簡略化したもの
10 # 処理が単純なので速い
end
end
class Gear
attr_reader :chainring, :cog, :wheel
def initialize(chainring:, cog:, wheel:)
@chainring = chainring
@cog = cog
@wheel = wheel
end
def gear_inches
ratio * wheel.width # Wheelクラスのインスタンスを受け取ってwidthを呼ぶ想定
end
def ratio
chainring / cog.to_f
end
end
# テスト
# wheel.widthの返り値を使うgear.gear_inchesメソッドがちゃんと動くかテストしたい
class GearTest < MiniTest::Unit::TestCase
def test_calculates_gear_inches
gear = Gear.new(
chainring: 52,
cog: 11,
wheel: DiameterDouble.new) # テストではテストダブルを使う
assert_in_delta(47.27,
gear.gear_inches, # DiameterDoubleはwidthを実装しているのでgear_inchesが実行できる
0.01)
end
end
```
```ruby=
# モックの例
# メソッドが呼ばれることをテストする例
class Gear
attr_reader :chainring, :cog, :wheel, :observer
def initialize(chainring:, cog:, wheel:, observer:)
@chainring = chainring
@cog = cog
@wheel = wheel
@observer = observer # Gearクラスは新たにobserverオブジェクトを受け取るという設定
end
def gear_inches
ratio * wheel.diameter
end
def ratio
chainring / cog.to_f
end
def set_cog(new_cog)
@cog = new_cog
changed
end
def set_chainring(new_chainring)
@chainring = new_chainring
changed
end
def changed
observer.changed(chainring, cog)
end
end
# テスト
# chainringやcogの値を変更したときにobserver.changedがちゃんと呼ばれることをテストしたい
class GearTest < Minitest::Test
def setup
@observer = Minitest::Mock.new # ここでobserverをモックとして作成
@gear = Gear.new(
chainring: 52, # ここを変更してテストする
cog: 11, # ここを変更してテストする
wheel: DiameterDouble.new,
observer: @observer) # ここでモックを渡す
end
def test_notifies_observers_when_cogs_change
@observer.expect(:changed, true, [52, 27]) # observer.changed(52, 27)が呼ばれてtrueを返すことをexpect
@gear.set_cog(27) # 中でchangedからobserver.changedが呼ばれるはず
@observer.verify
end
def test_notifies_observers_when_chainrings_change
@observer.expect(:changed, true, [42, 11])# observer.changed(42, 11)が呼ばれてtrueを返すことをexpect
@gear.set_chainring(42) # 中でchangedからobserver.changedが呼ばれるはず
@observer.verify
end
end
```
### 各自の疑問点や気づき、学んだこと
- @maimu_x2x
- モックやスタブを使うとコード間の結合を疎結合にできてメンテがしやすくなる
- `assert_empty` は配列が空かをチェックする
- 無意識に都合の良いデータを作る・・・やりがちw
- hikaru
- モックはメッセージが送られたことを証明するためのもの
- スタブは返す値を証明するためのもの?
- `stub(:メソッド名, 値)`でそのメソッドを読んだ時に返す値を指定できる
- @garammasala29
- verifyはベリファイ。expectとMinitest::Mockクラスのメソッド
- スタブ化はテストの代用品をつくること。モック、スタブ、言葉の整理
- `assert_not_empty`のnotを見落としていたし、次はテストが落ちるようにコードを書いていくものだと勘違いしていた。
- Saki
- スタブとモックの違い
- Hikaruさんのサンプルコードがわかりやすかった
- スタブはメソッドの戻り値を「このテストを実行している間だけこのメソッドの戻り値を◯◯にして」と変えるだけ
- モックは、↑+`verify`メソッドで呼び出されたかどうかを検証できる
- ダブルの中にスタブとモック
- バリデーションテストの`assert_empty`
- `assert_empty(event.errors[:start_at])`
- `event.errors[:start_at]`が空=エラーが発生してなければtrue=テストが通る
- empty=空=フォームに入力がされてないというイメージで混乱しました😂
- @fuwa
- ダブルはオブジェクトの代わりをするオブジェクト。スタブもモックもダブルの一種。
- stubメソッドは既存メソッドの戻り値を一時的に差し替えることができる。
- スタブは他のモデルへの依存を減らしてテストを書けるようにしてくれるらしい。
- @tomonari
- スタブもモックも概念がいまいち理解しきれないので調べてみたいと思いました。モックを作ってテストするほうがやや処理が重い?
- @haruguchi
- mockやってたのかぁ、API叩いたりするときに避けては通れぬテストだ
- まだあんまりわかってないや
- 4, 5部読みたいところだったんで楽しみ!(でも全部は参加できないが、、、)
### 本日の振り返り(よかった点・次回に向けての改善点・今の気分などなんでもOK)
- @maimu_x2x
- 今日3Dセキュアのテストをやったのですが、Kaigi on Railsの発表を聞いていたため背景や裏側がわかってやりやすかったです :chiken:
- 3Dセキュア〜♪
- @fuwa
- チーム開発でvue.jsのissueが一息ついたと思っていたら新たなるvueのissueが現れました
- 今日はスクワットをがんばったら腰が痛くなってしまって悲しいです
- お大事に🍵
- ありがとうございます〜
- @garammasala29
- Hikaruさんのテストコードわかりやすかったです!オブジェクト指向設計実践ガイド気になる
- 7章も終わりだ!
- hikaru
- 僕が貼ったコードがよくなかったので後で上のスタブとモック
-
- @haruguchi
- hikaruさんにお薦めいただいたマスタリングTCP/IP入門を購入しました(いつ読むねん)ありがとうございます! :gogo!:
- 健康のため毎日ランニングしています(3日目)
- Saki
- デザインの微調整が終われば自作サービスを提出できるぞ〜! :tada:
------
## 2022\-10\-27(木)
### 連絡事項や確認・相談
### タイムキーパー
- @fuwa
### ドライバー
- Saki
### 読んだところ
### 次回
### 自由に使う共有スペース
### 各自の疑問点や気づき、学んだこと
- hikaru
- なんかプラクティスでやったような遥かな記憶...
- `has_one_attached :image`で画像とモデルを紐付ける
- フォームのチェックボックスは0/1らしい
- ` ActiveRecord::Type::Boolean.new.cast(remove_image)`で真偽値に変換してくれる
- `image`をnilにすると画像まで削除する?
- `has_one_attached :image, dependent: false`とすると関連だけ削除する
- @maimu_x2x
- `ActiveRecord::Type::Boolean` を `new` して `cast` メソッドを呼ぶと0を `false` 1を `true` に型変換してくれる
- https://qiita.com/kazutosato/items/9073e76bdca669085fba
- ActiveStorageの課題で画像削除ができず、更新で実装したためやり方がわかってスッキリ
- @fuwa
- `has_one_attached :image` でレコード1件に対して画像を1件アップロードできるようになる。画像を複数アップロードしたい場合は`has_many_attached :image` を使う
- `ActiveRecord::Type::Boolean.new.cast(●●)` は`●`がfalseや0とかだとfalse、それ以外だとtrueを返す
- Saki
- `remove_image`は、イベント編集時にファイルがもともとアップロードされてたときに「ファイルを削除する」のチェックボタンにクリックしたら`1`、されてなかったら`0`が入るようになっている。
```ruby
#これはRUBYのアクセサメソッド
attr_accessor :remove_image
# Eventモデル更新前に処理が走る
before_save :remove_image_if_user_accept
# remove_imageがtrueになる値(この場合1)ならEvent保存時に画像削除。0ならfalseなのでこの処理は走らない。
def remove_image_if_user_accept
self.image = nil if ActiveRecord::Type::Boolean.new.cast(remove_image)
end
```
- `self.image`の`self`は更新しようとしてるイベントもモデルのインスタンス(レコード)
- アクセサメソッドが久々に出てきて分からなかったので復習せねば...
- ワシも
- https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
- @garammasala29
- `ActiveRecord::Type::Boolean`の`cast`メソッド。`f`とか`off`とかでfalseが返ってくる。JSのfalsyな値とも混同しているので復習したい
- ブログ書こう!宣伝してね!
- そもそもActiveStorageにこれまで全然触れてきてなかった(今回の輪読会もおやすみしていたところ)だったので、サンプルアプリ立ち上げで勉強しておきたい
- @haruguchi
- ActiveRecordはmigrateするときにカラム名と同名のアクセサメソッドを生やしてくれる
- ないものは今回のように自分でアクセサを定義してやる必要がある
- ActiveRecord::Type::Boolean初めて知った!
- チェックボックスの値変えるやつありました
- https://github.com/rails/rails/blob/984c3ef2775781d47efa9f541ce570daa2434a80/actionview/lib/action_view/helpers/form_helper.rb#L1335
- @tomonari
- remove_imageが0or1なのは何か理由があるのでしょうか?最初からBooleanでいい気もします。
### 本日の振り返り(よかった点・次回に向けての改善点・今の気分などなんでもOK)
- @maimu_x2x
- お昼にharuguchiさんがatcoderのモブプロをやってくださり、取り組み方がわかったのでちょっとずつやろうと思います💪
- :muscle:
- @garammasala29
- モブプロ羨ましいな〜楽しかったですかー?
- Saki
- ミートアップ楽しみです〜 トミーさんの卒業式:tada:
- 今日なんとしても自作サービス提出したいです〜〜〜 リリースブログはまだ...
- :muscle:
- :gogo:
- :oooo!:
- @fuwa
- ジムでワイヤレスイヤホンを落としてきてしまってがっかりしてます
- :tsurai:
-
- hikaru
- オブジェクト指向設計実践ガイドのサンプルコードを一応書いたんですけど、長くて分かりづらかったので読まないでいいです
- :yomu!:
- @haruguchi(読まなくて良い)
- ランニング(4日目) :gogo!:
-----
## 2022\-10\-28(金) 主催者私用のためお休みにさせてもらいます🙏
-----