紙一重の積み重ね

アラフォーのエンジニアがなれる最高の自分を目指して、学んだことをこつこつ情報発信するブログです。

【14章】Ruby on Railsチュートリアル演習まとめ&解答例【14.3 ステータスフィード】

はじめに

Ruby on Rails チュートリアル実例を使ってRailsを学ぼう 第4版の 14章 14.3 ステータスフィードの演習まとめ&解答例です。

個人の解答例なので、誤りがあればご指摘ください。

動作環境

  • cloud9
  • ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
  • Rails 5.0.0.1

14.3.1 動機と計画

本章での学び

【test】ステータスフィードのテスト

  • feed should have the right posts
    • fixtureから、michaelユーザの情報を取得する
    • fixtureから、archerユーザの情報を取得する
    • fixtureから、lanaユーザの情報を取得する
    • フォローしているユーザの投稿を確認
      • lanaのマイクロポストを取得
        • michaelのフィードに含まれていること
    • 自分自身の投稿を確認
      • michaelのマイクロポストを取得
        • michaelのフィードに含まれていること
    • フォローしていないユーザの投稿を確認
      • archerのマイクロポストを取得
        • michaelのフィードに含まれていないこと

上記を踏まえて実装する。

  test "feed should have the right posts" do
    michael = users(:michael)
    archer  = users(:archer)
    lana    = users(:lana)
    # フォローしているユーザの投稿を確認
    lana.microposts.each do |post_following|
      assert michael.feed.include?(post_following)
    end
    # 自分自身の投稿を確認
    michael.microposts.each do |post_self|
      assert michael.feed.include?(post_self)
    end
    # フォローしていないユーザの投稿を確認
    archer.microposts.each do |post_unfollowed|
      assert_not michael.feed.include?(post_unfollowed)
    end
  end

テストは現時点ではredとなる。

演習1

マイクロポストのidが正しく並んでいると仮定して (すなわち若いidの投稿ほど古くなる前提で)、図 14.22のデータセットでuser.feed.map(&:id)を実行すると、どのような結果が表示されるでしょうか? 考えてみてください。ヒント: 13.1.4で実装したdefault_scopeを思い出してください。

user自身とuserがフォローしているマイクロポストが昇順で取得できる。 default_scopeで降順になるように実装していれば、マイクロポストが降順で取得できる。

14.3.2 フィードを初めて実装する

本章での学び

【事前準備】Rubyのmapメソッド

mapメソッドは、&とメソッドに対応するシンボルを使った短縮表記ができる。

okoyan:~/workspace/sample_app (following-users) $ rails console
Running via Spring preloader in process 2367
Loading development environment (Rails 5.0.0.1)
>> [1,2,3,4].map { |i| i.to_s }
=> ["1", "2", "3", "4"]
>> 
?> [1,2,3,4].map(&:to_s)
=> ["1", "2", "3", "4"]
>> 

カンマ区切りの文字列としてつなげることもできる。

?> [1,2,3,4].map(&:to_s).join(', ')
=> "1, 2, 3, 4"

user.followingにある各要素のidを呼び出し、フォローしているユーザのidを配列とする。

?> User.first.following.map(&:id)
  User Load (0.6ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
  User Load (0.7ms)  SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ?  [["follower_id", 1]]
=> [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]

ActiveRecordで用意しているfollowing_idsメソッドを使うことで、上記と同じ結果が得られる。

?> User.first.following_ids
  User Load (0.2ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
   (0.3ms)  SELECT "users".id FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ?  [["follower_id", 1]]
=> [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]

応用して、カンマ区切りの文字列を取得することもできる。

?> User.first.following_ids.join(', ')
  User Load (0.3ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
   (0.4ms)  SELECT "users".id FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ?  [["follower_id", 1]]
=> "3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51"

【test】テスト実行

テスト実行時に以下のようなエラーが発生。

7ffe7035b000-7ffe7035d000 r--p 00000000 00:00 0                          [vvar]
7ffe7035d000-7ffe7035f000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]


[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html

エラーになるため、bundle updateを実行。

yokoyan:~/workspace/sample_app (following-users) $ bundle update
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Fetching gem metadata from https://rubygems.org/.........
Fetching version metadata from https://rubygems.org/..
Fetching dependency metadata from https://rubygems.org/.
Resolving dependencies.....
Using rake 12.0.0
Using CFPropertyList 2.3.5
Using concurrent-ruby 1.0.5
Installing i18n 0.8.6 (was 0.8.1)
Installing minitest 5.10.3 (was 5.10.1)
Using thread_safe 0.3.6
Using builder 3.2.3
Using erubis 2.7.0
Installing mini_portile2 2.2.0 (was 2.1.0)
Installing rack 2.0.3 (was 2.0.1)
Using nio4r 1.2.1
Using websocket-extensions 0.1.2
Using mime-types-data 3.2016.0521
Using arel 7.1.4
Using ansi 1.5.0
Using execjs 2.7.0
Using bcrypt 3.1.11
Installing rb-fsevent 0.10.2 (was 0.9.8)
Using ffi 1.9.18
Using will_paginate 3.1.0
Using bundler 1.14.6
Using byebug 9.0.0
Using coderay 1.1.1
Using coffee-script-source 1.12.2
Using method_source 0.8.2
Installing thor 0.20.0 (was 0.19.4)
Installing debug_inspector 0.0.3 (was 0.0.2) with native extensions
Using excon 0.58.0
Using formatador 0.2.5
Using multi_json 1.12.1
Using ipaddress 0.8.3
Using xml-simple 1.1.5
Using inflecto 0.0.2
Using json 1.8.6
Using trollop 2.1.2
Installing lumberjack 1.0.12 (was 1.0.11)
Using nenv 0.3.0
Using shellany 0.0.1
Using slop 3.6.0
Using guard-compat 1.2.1
Using mini_magick 4.7.0
Using ruby-progressbar 1.8.1
Using puma 3.4.0
Installing tilt 2.0.8 (was 2.0.6)
Using spring 1.7.2
Using sqlite3 1.3.11
Installing turbolinks-source 5.0.3 (was 5.0.0)
Using fission 0.5.0
Using faker 1.6.6
Installing tzinfo 1.2.3 (was 1.2.2)
Installing nokogiri 1.8.0 (was 1.7.0.1) with native extensions
Using rack-test 0.6.3
Using sprockets 3.7.1
Using websocket-driver 0.6.5
Using mime-types 3.1
Installing autoprefixer-rails 7.1.3 (was 6.7.7.1)
Using uglifier 3.0.0
Installing rb-inotify 0.9.10 (was 0.9.8)
Using bootstrap-will_paginate 0.0.10
Using coffee-script 2.4.1
Using fog-core 1.45.0
Using notiffany 0.1.1
Using pry 0.10.4
Using guard-minitest 2.4.4
Using minitest-reporters 1.1.9
Using turbolinks 5.0.1
Using activesupport 5.0.0.1
Using loofah 2.0.3
Using rbvmomi 1.11.3
Installing mail 2.6.6 (was 2.6.4)
Installing sass-listen 4.0.0
Using listen 3.0.8
Using fog-json 1.0.2
Using fog-xml 0.1.3
Using fog-local 0.3.1
Using fog-vmfusion 0.1.0
Installing rails-dom-testing 2.0.3 (was 2.0.2)
Installing globalid 0.4.0 (was 0.3.7)
Using activemodel 5.0.0.1
Using jbuilder 2.4.1
Using rails-html-sanitizer 1.0.3
Installing fog-vsphere 1.12.0 (was 1.11.3)
Installing sass 3.5.1 (was 3.4.23)
Using guard 2.13.0
Using spring-watcher-listen 2.0.0
Using fog-aliyun 0.2.0
Using fog-brightbox 0.13.0
Using fog-dnsimple 1.0.0
Using fog-openstack 0.1.21
Using fog-profitbricks 4.0.0
Using fog-sakuracloud 1.7.5
Using fog-serverlove 0.1.2
Using fog-softlayer 1.1.4
Using fog-storm_on_demand 0.1.1
Using fog-atmos 0.1.0
Installing fog-aws 1.4.1 (was 1.4.0)
Using fog-cloudatcost 0.1.2
Using fog-digitalocean 0.3.0
Using fog-dynect 0.0.3
Using fog-ecloud 0.3.0
Using fog-google 0.1.0
Using fog-powerdns 0.1.1
Using fog-rackspace 0.1.5
Using fog-radosgw 0.0.5
Using fog-riakcs 0.1.0
Using fog-terremark 0.1.0
Using fog-voxel 0.1.0
Using fog-xenserver 0.3.0
Using activejob 5.0.0.1
Using activerecord 5.0.0.1
Using carrierwave 1.1.0
Using actionview 5.0.0.1
Using bootstrap-sass 3.3.6
Using fog 1.40.0
Using actionpack 5.0.0.1
Using actioncable 5.0.0.1
Using actionmailer 5.0.0.1
Using railties 5.0.0.1
Using sprockets-rails 3.2.0
Using rails-controller-testing 0.1.1
Using coffee-rails 4.2.1
Using jquery-rails 4.1.1
Using web-console 3.1.1
Using rails 5.0.0.1
Using sass-rails 5.0.6
Bundle updated!
Gems in the group production were not installed.

【test】再度テストを実行

テスト結果がgreenになることを確認。

yokoyan:~/workspace/sample_app (following-users) $ rails test
Running via Spring preloader in process 15601
Started with run options --seed 9757

DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only                                                                                                                     ] 18% Time: 00:00:02,  ETA: 00:00:10
the following keyword arguments in future Rails versions:
params, headers, env, xhr, as

Examples:

get '/profile',
  params: { id: 1 },
  headers: { 'X-Extra-Header' => '123' },
  env: { 'action_dispatch.custom' => 'custom' },
  xhr: true,
  as: :json
 (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27)
  75/75: [====================================================================================================================================================================================================] 100% Time: 00:00:05, Time: 00:00:05

Finished in 5.33761s
75 tests, 335 assertions, 0 failures, 0 errors, 0 skips

演習1

リスト 14.44において、現在のユーザー自身の投稿を含めないようにするにはどうすれば良いでしょうか? また、そのような変更を加えると、リスト 14.42のどのテストが失敗するでしょうか?

user.feedの実行結果は以下の通り。 フォローしているユーザのIDと、自分のIDを渡している。

yokoyan:~/workspace/sample_app (following-users) $ rails console
Running via Spring preloader in process 1248
Loading development environment (Rails 5.0.0.1)
>> 
?> user = User.first
  User Load (0.4ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2017-08-26 04:58:19", updated_at: "2017-08-26 04:58:19", password_digest: "$2a$10$0FM4wRDzxU.nOpDa5RmxZuyTz/omIcX4Qm4nguWAde0...", remember_digest: nil, admin: true, activation_digest: "$2a$10$gQQiCXRCh4ZDsruNiSv5euZW4Vwt7kcKm6tZkxM1MMg...", activated: true, activated_at: "2017-08-26 04:58:19", reset_digest: nil, reset_sent_at: nil>
>> 
?> user.feed
   (0.6ms)  SELECT "users".id FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ?  [["follower_id", 1]]
  Micropost Load (3.2ms)  SELECT "microposts".* FROM "microposts" WHERE (user_id IN (3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51) OR user_id = 1) ORDER BY "microposts"."created_at" DESC

つまり、自分の投稿を含めないようにするには、2つ目の引数を削除する。

  def feed
    # Micropost.where("user_id = ?", id)
    # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
    Micropost.where("user_id IN (?) ", following_ids)
  end

以下のテストで結果がredとなる。

 FAIL["test_micropost_interface", MicropostsInterfaceTest, 1.9879938876256347]
 test_micropost_interface#MicropostsInterfaceTest (1.99s)
        Expected at least 1 element matching "div.pagination", found 0..
        Expected 0 to be >= 1.
        test/integration/microposts_interface_test.rb:14:in `block in <class:MicropostsInterfaceTest>'

 FAIL["test_feed_should_have_the_right_posts", UserTest, 3.517132450826466]
 test_feed_should_have_the_right_posts#UserTest (3.52s)
        Expected false to be truthy.
        test/models/user_test.rb:108:in `block (2 levels) in <class:UserTest>'
        test/models/user_test.rb:107:in `block in <class:UserTest>'

演習2

リスト 14.44において、フォローしているユーザーの投稿を含めないようにするにはどうすれば良いでしょうか? また、そのような変更を加えると、リスト 14.42のどのテストが失敗するでしょうか?

演習1と逆で、1つめの引数を削除する。 (試作フィードの状態に戻すことになる)

  def feed
    Micropost.where("user_id = ?", id)
    # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
  end

以下のテストがredになることを確認。

 FAIL["test_feed_should_have_the_right_posts", UserTest, 2.452872692141682]
 test_feed_should_have_the_right_posts#UserTest (2.45s)
        Expected false to be truthy.
        test/models/user_test.rb:104:in `block (2 levels) in <class:UserTest>'
        test/models/user_test.rb:103:in `block in <class:UserTest>'

演習3

リスト 14.44において、フォローしていないユーザーの投稿を含めるためにはどうすれば良いでしょうか? また、そのような変更を加えると、リスト 14.42のどのテストが失敗するでしょうか? ヒント: 自分自身とフォローしているユーザー、そしてそれ以外という集合は、いったいどういった集合を表すのか考えてみてください。

フォローしていないユーザーの投稿を含めるためには、 micropostsテーブルのすべての情報を取得する。

  def feed
    # Micropost.where("user_id = ?", id)
    # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
    Micropost.all
  end

以下のテストがredになることを確認。 フォローしていないユーザが含まれないことが期待値であるためエラーとなっている。

 FAIL["test_feed_should_have_the_right_posts", UserTest, 1.5933369509875774]
 test_feed_should_have_the_right_posts#UserTest (1.59s)
        Expected true to be nil or false
        test/models/user_test.rb:112:in `block (2 levels) in <class:UserTest>'
        test/models/user_test.rb:111:in `block in <class:UserTest>'

14.3.3 サブセレクト

本章での学び

今の実装ではパフォーマンスに懸念があるため、サブセレクトを使って解決する。

【model】feedメソッドのリファクタリング

同じ変数を複数の場所に挿入する場合は、whereメソッド内の変数にキーと値のペアにしたほうが便利とのこと。

  def feed
    # 自分のみ
    # Micropost.where("user_id = ?", id)
    # フォローしているユーザか自分の投稿
    # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
    Micropost.where("user_id IN (:following_ids) OR user_id = :user_id", following_ids: following_ids, user_id: id)
    # フォローしていないユーザも含める
    # Micropost.all
  end

上記からさらに、サブクエリのSQLを追加する。

 def feed
    # フォローしているユーザか自分の投稿
    # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
    # Micropost.where("user_id IN (:following_ids) OR user_id = :user_id", following_ids: following_ids, user_id: id)
    #最終的な実装
    following_ids = "SELECT followed_id FROM relationships WHERE follower_id = :user_id"
    Micropost.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: id)

動作確認

テストがgreenになることを確認。

yokoyan:~/workspace/sample_app (following-users) $ rails test
Running via Spring preloader in process 2913
Started with run options --seed 15753

DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only=====                                             ] 66% Time: 00:00:03,  ETA: 00:00:02
the following keyword arguments in future Rails versions:
params, headers, env, xhr, as

Examples:

get '/profile',
  params: { id: 1 },
  headers: { 'X-Extra-Header' => '123' },
  env: { 'action_dispatch.custom' => 'custom' },
  xhr: true,
  as: :json
 (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27)
  75/75: [=================================================================================================================================] 100% Time: 00:00:04, Time: 00:00:04

Finished in 4.46490s
75 tests, 335 assertions, 0 failures, 0 errors, 0 skips

ブラウザからもフォローしているユーザのフィードが表示されていることを確認。

image

MASTERブランチへのマージ

yokoyan:~/workspace/sample_app (master) $ rails test
yokoyan:~/workspace/sample_app (master) $ git add -A
yokoyan:~/workspace/sample_app (master) $ git status
yokoyan:~/workspace/sample_app (master) $ git commit -m "Add user following"
yokoyan:~/workspace/sample_app (master) $ git checkout master
yokoyan:~/workspace/sample_app (master) $ git merge following-users 

Herokuへのデプロイ

yokoyan:~/workspace/sample_app (master) $ git push
yokoyan:~/workspace/sample_app (master) $ git push heroku

HerokuのDBのマイグレーション

本番環境のDBのリセット、マイグレーション、テストデータの登録を行う。

yokoyan:~/workspace/sample_app (master) $ heroku pg:reset DATABASE                                                                                                              
yokoyan:~/workspace/sample_app (master) $ heroku run rails db:migrate
yokoyan:~/workspace/sample_app (master) $ heroku run rails db:seed

演習1

Homeページで表示される1ページ目のフィードに対して、統合テストを書いてみましょう。リスト 14.49はそのテンプレートです。

  • feed on Home page
    • getリクエストを送信する(root_path)
    • @userのフィードを生成し、1ページ目を取得する。micropostの数だけ繰り返す。
      • micropost内のcontentをエスケープして、HTML内に表示されること

上記を踏まえて実装する。

  test "feed on Home page" do
    get root_path
    @user.feed.paginate(page: 1).each do |micropost|
      assert_match CGI.escapeHTML(micropost.content), response.body
    end
  end

テスト結果がgreenであることを確認。

yokoyan:~/workspace/sample_app (master) $ rails test
Running via Spring preloader in process 1866
Started with run options --seed 52063

DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only                                    ] 68% Time: 00:00:03,  ETA: 00:00:02
the following keyword arguments in future Rails versions:
params, headers, env, xhr, as

Examples:

get '/profile',
  params: { id: 1 },
  headers: { 'X-Extra-Header' => '123' },
  env: { 'action_dispatch.custom' => 'custom' },
  xhr: true,
  as: :json
 (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27)
  76/76: [===================================================================================================================] 100% Time: 00:00:04, Time: 00:00:04

Finished in 4.53767s
76 tests, 395 assertions, 0 failures, 0 errors, 0 skips

演習2

リスト 14.49のコードでは、期待されるHTMLをCGI.escapeHTMLメソッドでエスケープしています (このメソッドは11.2.3で扱ったCGI.escapeと同じ用途です)。このコードでは、なぜHTMLをエスケープさせる必要があったのでしょうか? 考えてみてください。ヒント: 試しにエスケープ処理を外して、得られるHTMLの内容を注意深く調べてください。マイクロポストの内容が何かおかしいはずです。また、ターミナルの検索機能 (Cmd-FもしくはCtrl-F) を使って「sorry」を探すと原因の究明に役立つはずです。

content属性に含まれる改行コードを含め、エスケープするため。

エスケープ処理を外して検証。

  test "feed on Home page" do
    get root_path
    @user.feed.paginate(page: 1).each do |micropost|
      # assert_match CGI.escapeHTML(micropost.content), response.body
      assert_match micropost.content, response.body
    end
  end

テスト結果がredになることを確認。

yokoyan:~/workspace/sample_app (master) $ rails test
Running via Spring preloader in process 2044
Started with run options --seed 8292

 FAIL["test_feed_on_Home_page", FollowingTest, 2.3300648159347475]
 test_feed_on_Home_page#FollowingTest (2.33s)
        Expected /I'm\ sorry\.\ Your\ words\ made\ sense,\ but\ your\ sarcastic\ tone\ did\ not\./ to match "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Ruby on Rails Tutorial Sample App</title>\n        \n    <link rel=\"stylesheet\" media=\"all\" href=\"/assets/application-10fa38882e9521d0e9f81abaad450c57aa1940c84d7a510d6d14e6ade7210745.css\" data-turbolinks-track=\"reload\" />\n    <script src=\"/assets/application-8145d5d9dada575ad93c1a2586a1fff566ccef067d7d39e629336a3561895941.js\" data-turbolinks-track=\"reload\"></script>\n        <!--[if lt IE 9]>\n      <script scr=\"//cdnjs.cloudflare.com/ajax/libs/html5shiv/r29/html5.min.js\">\n      </script>\n    <![endif]-->\n  </head>\n  <body>\n        <header class=\"navbar navbar-fixed-top navbar-inverse\">\n      <div class=\"container\">\n        <a id=\"logo\" href=\"/\">sample app</a>\n        <nav>\n          <ul class=\"nav navbar-nav navbar-right\">\n            <li><a href=\"/\">Home</a></li>\n            <li><a href=\"/help\">Help</a></li>\n              <li><a href=\"/users\">Users</a></li>\n              <li class=\"dropdown\">\n              <a href=\"#\" class=\"dropdown-toggle\" data-toggle=\"dropdown\">\n                Account <b class=\"caret\"></b>\n              </a>\n              <ul class=\"dropdown-menu\">\n                <li><a href=\"/users/762146111\">Profile</a></li>\n                <li><a href=\"/users/762146111/edit\">Settings</a></li>\n                <li class=\"divider\"></li>\n                <li>\n                  <a rel=\"nofollow\" data-method=\"delete\" href=\"/logout\">Log out</a>\n                </li>\n              </ul>\n            </li>\n          </ul>\n        </nav>\n      </div>\n    </header>\n\n    <div class=\"container\">\n          <div class=\"row\">\n    <aside class=\"col-md-4\">\n      <section class=\"user_info\">\n        <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n<h1>Michael Example</h1>\n<span><a href=\"/users/762146111\">view my profile</a></span>\n<span>34 microposts</span>\n      </section>\n      <section class=\"stats\">\n        <div class=\"stats\">\n  <a href=\"/users/762146111/following\">\n    <strong id=\"following\" class=\"stat\">\n      2\n    </strong>\n    following\n  </a>\n  <a href=\"/users/762146111/followers\">\n    <strong id=\"followers\" class=\"stat\">\n      2\n    </strong>\n    followers\n  </a>\n</div>\n      </section>\n      <section>\n        <form class=\"new_micropost\" id=\"new_micropost\" enctype=\"multipart/form-data\" action=\"/microposts\" accept-charset=\"UTF-8\" method=\"post\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" />\n  \n  <div class=\"field\">\n    <textarea placeholder=\"Compose new micropost...\" name=\"micropost[content]\" id=\"micropost_content\">\n</textarea>\n  </div>\n  <input type=\"submit\" name=\"commit\" value=\"Post\" class=\"btn btn-primary\" data-disable-with=\"Post\" />\n  <span class=\"picture\">\n    <input accept=\"image/jpeg,image/gif,image/png\" type=\"file\" name=\"micropost[picture]\" id=\"micropost_picture\" />\n  </span>\n</form>\n<script type=\"text/javascript\">\n  $('#micropost_picture').bind('change', function() {\n    var size_in_megabytes = this.files[0].size/1024/1024;\n    if (size_in_megabytes > 5) {\n      alert('Maximum file size is 5MB. Please choose a smaller file.')\n    }\n  });\n</script>\n      </section>\n    </aside>\n    <div class=\"col-md-8\">\n      <h3>Micropost Feed</h3>\n        <ol class=\"microposts\">\n    <li id=\"micropost-941832919\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Writing a short test\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted less than a minute ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/941832919\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-3454773\">\n  <a href=\"/users/409608538\"><img alt=\"Lana Kane\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/de9a58df9617af487e8b28dbb3aa50de?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/409608538\">Lana Kane</a></span>\n  <span class=\"content\">\n    I&#39;m sorry. Your words made sense, but your sarcastic tone did not.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted 10 minutes ago.\n  </span>\n</li>\n<li id=\"micropost-499495288\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    I just ate an orange!\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted 10 minutes ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/499495288\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-12348100\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Sad cats are sad: http://youtu.be/PKffm2uI4dk\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 2 hours ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/12348100\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-970054474\">\n  <a href=\"/users/409608538\"><img alt=\"Lana Kane\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/de9a58df9617af487e8b28dbb3aa50de?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/409608538\">Lana Kane</a></span>\n  <span class=\"content\">\n    Dude, this van&#39;s, like, rolling probable cause.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 4 hours ago.\n  </span>\n</li>\n<li id=\"micropost-19959062\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Sunt ad blanditiis totam aut qui repudiandae rerum id sint.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/19959062\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-58620899\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Quidem doloremque qui consectetur architecto.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/58620899\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-68403196\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Explicabo aliquid voluptatem occaecati consectetur doloremque quia error pariatur id.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/68403196\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-71534927\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Velit error et aut non laboriosam dolorem aut.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/71534927\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-106776847\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Facilis quas et ut minima.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/106776847\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-177734013\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Voluptas consequatur enim aperiam qui.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/177734013\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-228979669\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Enim porro quam magni aliquid at dolores.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/228979669\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-234210660\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Consequatur tenetur mollitia dolores et.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/234210660\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-294621321\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Esse accusantium maxime dolore aut suscipit doloremque soluta ut at.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/294621321\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-328290505\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Omnis eius minima est distinctio voluptatem dolor impedit eum.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/328290505\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-352097504\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Quia et consectetur similique natus.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/352097504\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-406445230\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Et aut veniam aut similique consequatur qui est.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/406445230\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-444017243\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Quod sed est atque non dicta qui.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/444017243\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-488304196\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Aut consequuntur et et molestiae.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/488304196\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-525605047\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Ullam et voluptatem velit enim rerum.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/525605047\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-603694155\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Temporibus at omnis rerum voluptatem consectetur est culpa.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/603694155\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-613834836\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Reiciendis nam quod voluptates et at sit perspiciatis nihil.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/613834836\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-646488084\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Nobis et sunt mollitia natus saepe tempore.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/646488084\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-676538406\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Et molestiae quia blanditiis reiciendis illum sit aut enim.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/676538406\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-706600661\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Aut aliquam est provident nihil cum quaerat.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/706600661\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-762321612\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Sint dolor voluptatum blanditiis eveniet aut temporibus hic.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/762321612\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-792652861\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Error quaerat animi nulla nostrum iste nam.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/792652861\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-828012954\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Velit iste omnis laudantium voluptatibus.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/828012954\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-856985457\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Fugit dolorem quasi et temporibus omnis alias architecto.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/856985457\">delete</a>\n  </span>\n</li>\n<li id=\"micropost-860142042\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravator\" src=\"https://secure.gravatar.com/avatar/03ea78c0884c9ac0f73e6af7b9649e90?s=50\" /></a>\n  <span class=\"user\"><a href=\"/users/762146111\">Michael Example</a></span>\n  <span class=\"content\">\n    Architecto voluptatem voluptatem dolor sed.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted about 1 month ago.\n      <a data-confirm=\"You sure?\" rel=\"nofollow\" data-method=\"delete\" href=\"/microposts/860142042\">delete</a>\n  </span>\n</li>\n\n  </ol>\n    <div class=\"pagination\"><ul class=\"pagination\"><li class=\"prev previous_page disabled\"><a href=\"#\">&#8592; Previous</a></li> <li class=\"active\"><a rel=\"start\" href=\"/?page=1\">1</a></li> <li><a rel=\"next\" href=\"/?page=2\">2</a></li> <li class=\"next next_page \"><a rel=\"next\" href=\"/?page=2\">Next &#8594;</a></li></ul></div>\n\n    </div>\n  </div>\n\n\n      <footer>\n  <small>\n    The <a href=\"http://railstutorial.jp\">Ruby on Rails Tutorial</a>\n    by <a href=\"http://www.michaelhartl.com\">Michael Hartl</a>\n  </small>\n  <nav>\n    <ul>\n      <li><a href=\"/about\">About</a></li>\n      <li><a href=\"/contact\">Contact</a></li>\n      <li><a href=\"http://news.railstutorial.org/\">News</a></li>\n    </ul>\n  </nav>\n</footer>\n      \n    </div>\n  </body>\n</html>\n".
        test/integration/following_test.rb:65:in `block (2 levels) in <class:FollowingTest>'
        test/integration/following_test.rb:63:in `block in <class:FollowingTest>'

DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only============                        ] 80% Time: 00:00:03,  ETA: 00:00:01
the following keyword arguments in future Rails versions:
params, headers, env, xhr, as

Examples:

get '/profile',
  params: { id: 1 },
  headers: { 'X-Extra-Header' => '123' },
  env: { 'action_dispatch.custom' => 'custom' },
  xhr: true,
  as: :json
 (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27)
  76/76: [===================================================================================================================] 100% Time: 00:00:04, Time: 00:00:04

Finished in 4.62458s
76 tests, 339 assertions, 1 failures, 0 errors, 0 skips

おわりに

ついにRailsチュートリアルのアプリケーションが完成しました! 4月からこつこつ続けてきて約4ヶ月で完走です。 RubyもRailsも知識ゼロでしたが、自分の手で作り上げていくのはとても楽しかったです。

新しいことを学ぶのに、年齢は関係ないですね。

素晴らしい教材を提供してくださったMichaelさん、安川さんに感謝です!