• 検索結果がありません。

No route matches というエラーが出てきました。前に変更したconfig/routes.rb の設定を思い出してください。

ドキュメント内 Rails1.indd (ページ 93-100)

username:~/workspace (master) $ rails routes | grep taggings | grep new new_book_tagging GET /books/:book_id/

taggings/new(.:format) taggings#new

taggingsは、booksに依存して呼ぶように変更しましたので、テストでも同じ修正が必 要です。テストでは、この部分でアクションを呼んでいます。

get :new, params: {}, session: valid_session

これを次のように変更しましょう。

get :new, params: { book_id: book.id }, session: valid_session

params:

の後ろのハッシュに、URLで必要なパラメータを与えることができます。すべ ての tagging は books に依存していますから、

editcreateupdatedestroyも

すべてbook_idを与えるように修正してください。

そうしてテストを実行すると、また別のエラーが出てきました。

username:~/workspace (master) $ rspec spec/controllers/taggings_controller_spec.rb ..FFFFFFFF

Failures:

1) TaggingsController POST #create with valid params creates a new Tagging Failure/Error: format.html { redirect_to @tagging,

notice: 'Tagging was successfully created.' } NoMethodError:

undefined method tagging_url' for #<TaggingsController:0x00000003fb7428>

Did you mean? tag_url

# /usr/local/rvm/gems/ruby-2.3.0/gems/turbolinks-5.0.1/lib/turbolinks/

redirection.rb:12:in redirect_to'

# ./app/controllers/taggings_controller.rb:32:in block (2 levels) in create' # ./app/controllers/taggings_controller.rb:30:in create'

# /usr/local/rvm/gems/ruby-2.3.0/gems/rails-controller-testing-1.0.1/lib/

rails/controller/testing/template_assertions.rb:61:in process' # /usr/local/rvm/gems/ruby-2.3.0/gems/rails-controller-testing-1.0.1/lib/

rails/controller/testing/integration.rb:12:in block (2 levels) in <module:Integration>'

# ./spec/controllers/taggings_controller_spec.rb:57:in block (5 levels) in

<top (required)>'

# ./spec/controllers/taggings_controller_spec.rb:56:in block (4 levels) in

<top (required)>' (以下省略)



undefined method 'tagging_url'

というエラーが出てきました。前に設定した

app/controllers/taggings_controller.rb

の

redirect_to

を思い出して ください。

format.html { redirect_to @book, notice:

'Tagging was successfully created.' }

create



update



destroy

のあとは

books#show

にリダイレクトしますから、テストコー ドのリダイレクトを以下のように変更しましょう。

expect(response).to redirect_to(book_path(book))

これでテストを実行して、全て通ることを確認できたらOKです。

• taggingsコントローラのリファクタリング(DRY原則とテストの恩恵)

app/controllers/taggings_controller.rb

を 見 てくだ さ い。

@book = Book.find(params[:book_id])

でのが繰り返し出てくるのがわかると思います。こ れはDRY原則(Don'tRepeatYourself)に反しますから、リファクタリングをしましょう。

リファクタリングをするときには、テストがあることが望ましいです。そのほうが同じ機能を 満たしていることが保証されますし、他のコードを壊してしまう心配もありません。ここでは、

同じ機能を持つところを抜き出してメソッドにし、必要であれば抽象化をして、該当するとこ ろでそのメソッドを呼び出す、という流れになります。では、まず抜き出してメソッドを作り ましょう。実は、コントローラは同じようなメソッドがすでにあります。

def set_tagging

@tagging = Tagging.find(params[:id]) end

これと同じように、@bookを作成してくれるメソッドを作ります。

def set_book

@book = Book.find(params[:book_id]) end

これを該当するところで呼び出せばいいのですが、set_taggingと同じように呼び出し たほうが一貫性があって後からわかりやすいと思います。呼び出すところは、コントローラ ファイルの一番最初の部分です。

before_action :set_tagging, only: [:edit, :update, :destroy]



同じように before_action で呼び出しましょう。

@book は全てのアクションで使用し

ますから、

only:

やexcept: は必要ありません。

before_action :set_book

そして、各アクションの@book の行を忘れずに削除してください。これでテストを実行して、

エラーがなければうまくリファクタリングできています。少しコードがすっきりしてかっこよく なりましたね。

• ルーティングテスト

次はルーティングテストです。まずはテストを実行してみましょう。

username:~/workspace (master) $ rspec spec/routing/

...FFFFFFFF..F...

Failures:

1) TaggingsController routing routes to #index

Failure/Error: expect(:get => "/taggings").to route_to("taggings#index") No route matches "/taggings"

# ./spec/routing/taggings_routing_spec.rb:7:in block (3 levels) in <top (required)>' 2) TaggingsController routing routes to #new

Failure/Error: expect(:get => "/taggings/new").to route_to("taggings#new") No route matches "/taggings/new"

# ./spec/routing/taggings_routing_spec.rb:11:in block (3 levels) in <top (required)>' 3) TaggingsController routing routes to #show

Failure/Error: expect(:get => "/taggings/1").to route_to("taggings#show",

:id => "1")

No route matches "/taggings/1"

# ./spec/routing/taggings_routing_spec.rb:15:in block (3 levels) in <top (required)>' 4) TaggingsController routing routes to #edit

Failure/Error: expect(:get => "/taggings/1/edit").to route_to("taggings#edit",

:id => "1")

No route matches "/taggings/1/edit"

# ./spec/routing/taggings_routing_spec.rb:19:in block (3 levels) in <top (required)>' 5) TaggingsController routing routes to #create

Failure/Error: expect(:post => "/taggings").to route_to("taggings#create") No route matches "/taggings"

# ./spec/routing/taggings_routing_spec.rb:23:in block (3 levels) in <top (required)>' 6) TaggingsController routing routes to #update via PUT

Failure/Error: expect(:put => "/taggings/1").to route_to("taggings#update",

:id => "1")

No route matches "/taggings/1"

# ./spec/routing/taggings_routing_spec.rb:27:in block (3 levels) in <top (required)>' 7) TaggingsController routing routes to #update via PATCH

Failure/Error: expect(:patch => "/taggings/1").to route_to("taggings#update",

:id => "1")

No route matches "/taggings/1"



8) TaggingsController routing routes to #destroy

Failure/Error: expect(:delete => "/taggings/1").to route_to("taggings#destroy",

:id => "1")

No route matches "/taggings/1"

# ./spec/routing/taggings_routing_spec.rb:35:in block (3 levels) in <top (required)>' 9) TagsController routing routes to #show

Failure/Error: expect(:get => "/tags/1").to route_to("tags#show", :id => "1") No route matches "/tags/1"

# ./spec/routing/tags_routing_spec.rb:15:in block (3 levels) in <top (required)>' Finished in 0.04067 seconds (files took 1.87 seconds to load)

24 examples, 9 failures Failed examples:

rspec ./spec/routing/taggings_routing_spec.rb:6 # TaggingsController routing routes to #index

rspec ./spec/routing/taggings_routing_spec.rb:10 # TaggingsController routing routes to #new

rspec ./spec/routing/taggings_routing_spec.rb:14 # TaggingsController routing routes to #show

rspec ./spec/routing/taggings_routing_spec.rb:18 # TaggingsController routing routes to #edit

rspec ./spec/routing/taggings_routing_spec.rb:22 # TaggingsController routing routes to #create

rspec ./spec/routing/taggings_routing_spec.rb:26 # TaggingsController routing routes to #update via PUT

rspec ./spec/routing/taggings_routing_spec.rb:30 # TaggingsController routing routes to #update via PATCH

rspec ./spec/routing/taggings_routing_spec.rb:34 # TaggingsController routing routes to #destroy

rspec ./spec/routing/tags_routing_spec.rb:14 # TagsController routing routes to #show



コントローラテストのところで見たようなエラーもありますね。

No route matches "/taggings/1"

これ は もう説 明 するまで も な い でしょう。例 えば、spec/routing/taggings_

routing_spec.rb

の中で、

expect(:get => "/taggings/new").to route_to("taggings#new")



このように /taggings となっているところをすべて /books/:book_id/taggings に置き換えてください。このとき、

:book_id

にあたる部分の数字は、テスト用データの 値であれば何でもかまいません。まずは、

book とそれに紐づく tagging を呼び出し

ます。

let(:book) { Book.first }

let(:tagging) { book.taggings.first }

これらをURLのidとして渡すのですが、文字列の中にRubyを書くときは、ダブルクォーテー ション+シャープ+波カッコで囲むのでした。ですので、このテスト全体は以下の通りにな ります。indexアクションとshowアクションはありませんから、忘れずに削除してください。

require "rails_helper"

RSpec.describe TaggingsController, type: :routing do describe "routing" do

let(:book) { Book.first }

let(:tagging) { book.taggings.first } it "routes to #new" do

expect(:get => "/books/#{book.id}/taggings/new").to route_to("taggings#new", :book_id => "#{book.id}") end

it "routes to #edit" do

expect(:get => "/books/#{book.id}/taggings/#{tagging.id}/edit").to route_to("taggings#edit", :id => "#{tagging.id}",

:book_id => "#{book.id}") end

it "routes to #create" do

expect(:post => "/books/#{book.id}/taggings").to

route_to("taggings#create", :book_id => "#{book.id}") end

it "routes to #update via PUT" do

expect(:put => "/books/#{book.id}/taggings/#{tagging.id}").to route_to("taggings#update", :id => "#{tagging.id}", :book_id => "#{book.id}")

end

 it "routes to #update via PATCH" do

expect(:patch => "/books/#{book.id}/taggings/#{tagging.id}").to route_

to("taggings#update", :id => "#{tagging.id}", :book_id => "#{book.id}") end

it "routes to #destroy" do

expect(:delete => "/books/#{book.id}/taggings/#{tagging.id}").to route_

to("taggings#destroy", :id => "#{tagging.id}", :book_id => "#{book.id}") end

end end



ルーティングのもう一つのエラーは、tags#showに対するエラーです。このアクションも 削除しましたので、spec/routing/tags_routing_spec.rbから削除してしまって ください。

これでルーティングのテストはすべてOKのはずです。

commit

• ビューテスト

ビューのテストは、後で説明するリクエストテストである程度代用できることや、修正の頻度 の高さから、Rubyでここまで書くプロジェクトは少ないようです。また、今回の1stリリー スでは扱いませんが、javascriptを多用するような場合においては、このテストではなく、

gemなどを用いて別のファイルを用意したりします。ここでは、簡単に修正ポイントだけご 紹介しておきます。

まず、何度も出てきますが、コントローラで削除したアクションに対応するビューはファイル ごと削除してください。

削除対象

-- spec/views/taggings/show.html.erb_spec.rb -- spec/views/taggings/

index.html.erb_spec.rb -- spec/views/taggings/show.html.erb_spec.rb

このとき、

app/views 以下に該当のファイルが残っている場合も削除しましょう。

また、spec/views/books/index.html.erb_spec.rb の中で、一覧表の表示内 容を確認している部分があります。

assert_select "tr>td", :text => 2.to_s, :count => 2

いま、

Bookの一覧ページには価格を表示していませんので、この確認は削除しましょう。

それから、taggings#new と

taggings#edit

では form_for のPOSTをする URLの変更と、タグ名の入力フォームをセレクトボックスに変更しましたので、以下のとおり 修正してください。

spec/views/taggings/new.html.erb_spec.rb

ドキュメント内 Rails1.indd (ページ 93-100)

関連したドキュメント