マイクロポストの順序付けに対するテスト
まず、「最も新しいマイクロポストが、RDBから最初に取り出されるようにする」を確認するためのテストを書いていきます。
まず準備段階として、「fixture上に存在する、most_recent
という識別子を持ったマイクロポストが最初に表示されるようにする」というテストを実装します。
test"order should be most recent first"doassert_equalmicroposts(:most_recent),Micropost.firstend
必要なfixtureが定義されていないため、現時点でテストは成功しない
上記テストを実装した時点でテストを実行してみます。
# rails test test/models/micropost_test.rb
Running via Spring preloader in process 1532
Started with run options --seed 54438
ERROR["test_order_should_be_most_recent_first", MicropostTest, 0.8859796000033384]
test_order_should_be_most_recent_first#MicropostTest (0.89s)
StandardError: StandardError: No fixture named 'most_recent' found for fixture set 'microposts'
test/models/micropost_test.rb:29:in `block in <class:MicropostTest>'
5/5: [===================================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.89262s
5 tests, 4 assertions, 0 failures, 1 errors, 0 skips
StandardError: No fixture named 'most_recent' found for fixture set 'microposts'
「most_recent
という名前のfixtureが存在しない」というエラーメッセージですね。
必要なfixtureを定義する
まずは、必要なfixtureを定義する必要があります。対象のファイルはtest/fixtures/microposts.yml
です。
orange:content:"Ijustateanorange!"created_at:<%= 10.minutes.ago %>tau_manifesto:content:"Checkoutthe@taudaysiteby@mhartl:http://tauday.com"created_at:<%= 3.years.ago %>cat_video:content:"Sadcatsaresad:http://youtu.be/PKffm2uI4dk"created_at:<%= 2.hours.ago %>most_recent:content:"Writingashorttest"created_at:<%= Time.zone.now %>
「most_recent
という識別子を持ったマイクロポストが、最も新しいcreated_at
の値を持つ」という内容になっていますね。
fixture内では、マジックカラムの値を手動で更新することができる
「埋め込みRubyを使ってcreated_at
に直接値を設定している」というのは、test/fixtures/microposts.yml
における重要なポイントです。
通常、created_at
やupdated_at
といったマジックカラムには、直接値を設定することはできません。これらの属性に対する値は、通常はRailsによって自動設定されるものです。しかしながら、fixtureファイル中においては、これらの属性に対する値を直接設定することが可能なのです。
必要なfixtureを定義したところで、再びテストを実行してみる
# rails test test/models/micropost_test.rb
Running via Spring preloader in process 1559
Started with run options --seed 16916
FAIL["test_order_should_be_most_recent_first", MicropostTest, 0.3459459000005154]
test_order_should_be_most_recent_first#MicropostTest (0.35s)
--- expected
+++ actual
@@ -1 +1 @@
-#<Micropost id: 941832919, content: "Writing a short test", user_id: nil, created_at: "2019-12-21 09:08:30", updated_at: "2019-12-21 09:08:30">
+#<Micropost id: 12348100, content: "Sad cats are sad: http://youtu.be/PKffm2uI4dk", user_id: nil, created_at: "2019-12-21 07:08:30", updated_at: "2019-12-21 09:08:30">
test/models/micropost_test.rb:29:in `block in <class:MicropostTest>'
5/5: [===================================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.94467s
5 tests, 5 assertions, 1 failures, 0 errors, 0 skips
テスト失敗時のメッセージに変化がありました。「最も新しいマイクロポストが、最初に取り出されていない」という趣旨のメッセージが出力されています。
「最も新しいマイクロポストが、RDBから最初に取り出されるようにする」という実装
Railsのdefault_scope
メソッドをMicropostモデルで呼び出せば、マイクロポストを常に特定の順番で取り出せるようになります。「新しい順に取り出す」という場合は、「created_at
の降順に取り出す」ようにします。
4.0以降のRailsでは、以下のように記述すれば、「created_at
の降順に取り出す」という取り出し順序を実現できます。
default_scope->{order(created_at: :desc)}
「ラムダ式(もしくは無名関数)」なる文法要素
Rubyにおいて、->
(もしくはlambda
)というのは、「与えられたブロックから手続きオブジェクト(Procのインスタンス)を生成して返す」という関数です(Rubyリファレンスマニュアルより)。
>> -> { puts "foo" }
=> #<Proc:0x00007f086c61bba8@(irb):1 (lambda)>
>> -> { puts "foo" }.call
foo
=> nil
重要なのは以下の点でしょうか。
-> { puts "foo" }
という呼び出しは、Procオブジェクトを返す- Procオブジェクトの
call
メソッドを呼ぶと、ブロック内の処理が評価される
Procとかラムダ式とかクロージャーとか、このあたりの背景については全く理解できていないので、改めて学習する必要があると考えています。
default_scope
の引数として、ラムダ式が使われている
「引数としてラムダ式が使われている」というのは、default_scope
の大きな特徴です。
->{hogehoge(fuga)}
default_scope
におけるラムダ式の使い方としては、「order(created_at: :desc)
というメソッドそのものを引数として取る」というところでしょうか。
Micropostモデルにdefault_scope
を追加した時点でのテストの結果
上記実装をMicropostモデルに追加したところで、test/models/micropost_test.rb
に対するテストを再び実行します。
# rails test test/models/micropost_test.rb
Running via Spring preloader in process 1584
Started with run options --seed 38140
5/5: [===================================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.78921s
5 tests, 5 assertions, 0 failures, 0 errors, 0 skips
今度はテストが成功しました。