0
0

More than 3 years have passed since last update.

Railsチュートリアル 第13章 ユーザーのマイクロポスト - 「マイクロポストが投稿日時の降順(=新しい順)に表示されるようにする」という実装を、Micropostモデルに追加する

Posted at

マイクロポストの順序付けに対するテスト

まず、「最も新しいマイクロポストが、RDBから最初に取り出されるようにする」を確認するためのテストを書いていきます。

まず準備段階として、「fixture上に存在する、most_recentという識別子を持ったマイクロポストが最初に表示されるようにする」というテストを実装します。

test "order should be most recent first" do
  assert_equal microposts(:most_recent), Micropost.first
end

必要な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です。

test/fixtures/microposts.yml
orange:
  content: "I just ate an orange!"
  created_at: <%= 10.minutes.ago %>

tau_manifesto:
  content: "Check out the @tauday site by @mhartl: http://tauday.com"
  created_at: <%= 3.years.ago %>

cat_video:
  content: "Sad cats are sad: http://youtu.be/PKffm2uI4dk"
  created_at: <%= 2.hours.ago %>

most_recent:
  content: "Writing a short test"
  created_at: <%= Time.zone.now %>

most_recentという識別子を持ったマイクロポストが、最も新しいcreated_atの値を持つ」という内容になっていますね。

fixture内では、マジックカラムの値を手動で更新することができる

「埋め込みRubyを使ってcreated_atに直接値を設定している」というのは、test/fixtures/microposts.ymlにおける重要なポイントです。

通常、created_atupdated_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

今度はテストが成功しました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0