Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

5. コントローラスペックの基礎 #4

Open
iberianpig opened this issue Jan 23, 2015 · 45 comments
Open

5. コントローラスペックの基礎 #4

iberianpig opened this issue Jan 23, 2015 · 45 comments

Comments

@iberianpig
Copy link

コントローラは不憫です。なぜならRails開発者は薄い(skinny)コントローラを良しとして(これは良いことですが)、あまりテストを書こうとしないからです(これは良くないです。このあと詳しく説明します)。しかし、私たちはもっとテストのカバレッジを上げていこうとしているので、次はコントローラのテストをターゲットにしましょう。
コントローラのテストで難しいところの一つは、コントローラがたくさんの要素に依存していることです。たとえばモデル同士の関連の仕方やルーティングの設定によって依存関係の複雑さが変わってきます。本章ではこうした難しさに対処していきます。一度全体を理解できれば、今後自分のソフトウェアでどのようにコントローラのスペックを書けば良いのかはっきり理解できるはずです。がんばりましょう。
この章ではやや早足で次のような内容を説明します。

  • 最初に、なぜコントローラをテストすべきなのか、という点について考えてみます。
  • 続いてコントローラのテストの基礎を説明します( コントローラスペックはただの単体テストに すぎない 、という説明です)。
  • 次にコントローラスペックを整理して、アウトラインっぽくまとめます。
  • それからファクトリを使ってスペック用のデータをセットアップします。
  • そのあと、コントローラで一般的な7つのCRUDメソッドをテストします。また、非CRUDなメソッドの例も続けて見ていきます。
  • 次に入れ子になったルーティング(nested routes)を説明します。
  • 最後に、CSVやJSONのような非HTMLメソッドをテストして締めくくります。
@tatsuyard
Copy link

なぜコントローラをテストするのか

@tatsuyard
Copy link

薄いコントローラ
http://qiita.com/aDaichiOta/items/3fa5bc302565bcd495a8

@iberianpig
Copy link
Author

http://www.techscore.com/tech/Ruby/Rails/other/designpattern/2/
これとかわかりやすいかも

@tatsuyard
Copy link

@iberianpig
Copy link
Author

@iberianpig
Copy link
Author

http://morizyun.github.io/blog/ruby-design-pattern-09-decorator/
デコレータとは

2015-01-24 9:04 GMT+09:00 tatsuyard [email protected]:

オープンソースプロジェクト例
https://github.com/discourse/discourse


Reply to this email directly or view it on GitHub
#4 (comment)
.


山田 昂平 Kohei Yamada
tel:080-5263-9752
e-mail:[email protected]


@iberianpig
Copy link
Author

@iberianpig
Copy link
Author

次は!

整理

@tatsuyard
Copy link

整理から

@iberianpig
Copy link
Author

expect (response).to render_template :show
こんなの初めて

@tatsuyard
Copy link

match_arrayは配列の中身を確認しますが、順番は確認しません。順番が重要になる場合は代わりにeq使う

@tatsuyard
Copy link

POSTリクエストをテストする

から

@tatsuyard
Copy link

postリクエストでは、params[:contact]に相当するものを渡す

@iberianpig
Copy link
Author

最後に、 expect の使い方が最初のexampleのときとわずかに異なる点に注目してください。今回はHTTPリクエスト全体を expect のブロックに渡しています。これはこれまで見てきた expect の使い方よりも少し複雑です。このHTTPリクエストはProcとして渡され、その結果が実行前と実行後の両方で評価されます。こうすると予期した変化が起きたかどうか(このexampleでは起き なかった かどうか、も)をシンプルに判定できます。

@iberianpig
Copy link
Author

procようわからん

@tatsuyard
Copy link

patch と putの違いって?
参考
http://blog.kotamiyake.me/2013/12/09/defference-between-put-and-patch/

・PUTメソッドは既存のリソースに対して、リソースの一部の変更であってもリソース全体を新たなバージョンとして更新を行うことを要求する。

・一方、PATCHメソッドは既存のリソースに対して、リソースの一部分の更新を行うことを要求する。

@tatsuyard
Copy link

SOAP

異なるコンピュータ上で動作するプログラム同士がネットワークを通じてメッセージを伝え合い、連携して動作するための通信プロトコル(規約)の一つ。メッセージの記述にXMLを、データ伝送に主にHTTPを用い、Webサービスの提供や利用に適しているとされる。

...
SOAPの仕様は1999年にMicrosoft社などが発表したもので、2000年5月にW3Cが最初の標準規格を勧告した。00年台前半には企業システムのWebサービス化とともに普及が進展するかに思われたが、WS-*プロトコル群などを含む巨大で複雑な仕様が次第に敬遠されるようになり、RESTと呼ばれるシンプルな設計原則に則った軽量なWebサービス仕様(RESTful APIとも呼ばれる)のほうが好まれるようになった。

@tatsuyard
Copy link

  describe "POST #create" do
    before :each do
      @phones = [
        attributes_for(:phone),
        attributes_for(:phone),
        attributes_for(:phone)
      ]
    end

    # 有効な属性の場合
    context "with valid attributes" do
      # データベースに新しい連絡先を保存すること
      it "saves the new contact in the database" do
        expect{
          post :create, contact: attributes_for(:contact,
            phones_attributes: @phones)
        }.to change(Contact, :count).by(1)
      end

      # contacts#show にリダイレクトすること
      it "redirects to contacts#show" do
        post :create, contact: attributes_for(:contact,
          phones_attributes: @phones)
        expect(response).to redirect_to contact_path(assigns[:contact])
      end
    end

    # 無効な属性の場合
    context "with invalid attributes" do
      # データベースに新しい連絡先を保存しないこと
      it "does not save the new contact in the database" do
        expect{
          post :create,
            contact: attributes_for(:invalid_contact)
        }.not_to change(Contact, :count)
      end

      # :new テンプレートを再表示すること
      it "re-renders the :new template" do
        post :create,
          contact: attributes_for(:invalid_contact)
        expect(response).to render_template :new
      end
    end
  end

@tatsuyard
Copy link

  describe 'PATCH #update' do
    before :each do
      @contact = create(:contact,
        firstname: 'Lawrence',
        lastname: 'Smith')
    end

    # 有効な属性の場合
    context "with valid attributes" do
      p @contact
      # データベースの連絡先を更新すること
      it "locates the requested @contact" do
        patch :update, id: @contact, contact: attributes_for(:contact)
        expect(assigns(:contact)).to eq(@contact)
      end

      # @contact の属性を変更すること
      it "changes @contact's attributes" do
        patch :update, id: @contact,
          contact: attributes_for(:contact,
            firstname: 'Larry',
            lastname: 'Smith')
        @contact.reload
        expect(@contact.firstname).to eq('Larry')
        expect(@contact.lastname).to eq('Smith')
      end

      # 更新した連絡先のページへリダイレクトすること
      it "redirects to the updated contact" do
        patch :update, id: @contact, contact: attributes_for(:contact)
        expect(response).to redirect_to @contact
      end
    end

@tatsuyard
Copy link

updateメソッドテストでの無効な属性の場合、
expect {} Procが使えないのはなんでなん?

@tatsuyard
Copy link

Started PATCH "/contacts/1" for 127.0.0.1 at 2015-02-21 09:04:41 +0900
Processing by ContactsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ejRhQoXp+hUl0OFNXZx0DEN5rFVCema8ET6LOiL8Is8=", "contact"=>{"firstname"=>"eroero", "lastname"=>"nitta", "email"=>"[email protected]", "phones_attributes"=>{"0"=>{"phone_type"=>"home", "phone"=>"0120-11-22-33", "id"=>"1"}, "1"=>{"phone_type"=>"office", "phone"=>"98-76-54", "id"=>"2"}, "2"=>{"phone_type"=>"mobile", "phone"=>"000-111-222", "id"=>"3"}}}, "commit"=>"Update Contact", "id"=>"1"}
Contact Load (0.1ms) SELECT "contacts".* FROM "contacts" WHERE "contacts"."id" = ? LIMIT 1 ["id", 1] begin transaction
Phone Load (0.4ms) SELECT "phones".* FROM "phones" WHERE "phones"."contact_id" = ? AND "phones"."id" IN (1, 2, 3) [["contact_id", 1]]
Contact Exists (0.1ms) SELECT 1 AS one FROM "contacts" WHERE ("contacts"."email" = '[email protected]' AND "contacts"."id" != 1) LIMIT 1
Phone Load (0.1ms) SELECT "phones".* FROM "phones" WHERE "phones"."contact_id" = ? [["contact_id", 1]]
SQL (0.5ms) UPDATE "contacts" SET "firstname" = ?, "updated_at" = ? WHERE "contacts"."id" = 1 ["firstname", "eroero"], ["updated_at", "2015-02-21 00:04:41.747309"] commit transaction
Redirected to http://localhost:3000/contacts/1
Completed 302 Found in 263ms (ActiveRecord: 23.3ms)

Started GET "/contacts/1" for 127.0.0.1 at 2015-02-21 09:04:41 +0900
Processing by ContactsController#show as HTML
Parameters: {"id"=>"1"}
Contact Load (0.1ms) SELECT "contacts".* FROM "contacts" WHERE "contacts"."id" = ? LIMIT 1 [["id", 1]]
Phone Exists (0.1ms) SELECT 1 AS one FROM "phones" WHERE "phones"."contact_id" = ? LIMIT 1 [["contact_id", 1]]
Phone Load (0.1ms) SELECT "phones".* FROM "phones" WHERE "phones"."contact_id" = ? [["contact_id", 1]]
Rendered contacts/show.html.erb within layouts/application (1.7ms)
Completed 200 OK in 116ms (Views: 106.7ms | ActiveRecord: 0.3ms)

Started GET "/assets/jquery.js?body=1" for 127.0.0.1 at 2015-02-21 09:04:41 +0900

@tatsuyard
Copy link

Started PATCH "/contacts/1" for 127.0.0.1 at 2015-02-21 09:11:00 +0900
Processing by ContactsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ejRhQoXp+hUl0OFNXZx0DEN5rFVCema8ET6LOiL8Is8=", "contact"=>{"firstname"=>"eroero", "lastname"=>"", "email"=>"[email protected]", "phones_attributes"=>{"0"=>{"phone_type"=>"home", "phone"=>"0120-11-22-33", "id"=>"1"}, "1"=>{"phone_type"=>"office", "phone"=>"98-76-54", "id"=>"2"}, "2"=>{"phone_type"=>"mobile", "phone"=>"000-111-222", "id"=>"3"}}}, "commit"=>"Update Contact", "id"=>"1"}
Contact Load (0.2ms) SELECT "contacts".* FROM "contacts" WHERE "contacts"."id" = ? LIMIT 1 ["id", 1] begin transaction
Phone Load (0.2ms) SELECT "phones".* FROM "phones" WHERE "phones"."contact_id" = ? AND "phones"."id" IN (1, 2, 3) [["contact_id", 1]]
Contact Exists (0.1ms) SELECT 1 AS one FROM "contacts" WHERE ("contacts"."email" = '[email protected]' AND "contacts"."id" != 1) LIMIT 1
Phone Load (0.1ms) SELECT "phones".* FROM "phones" WHERE "phones"."contact_id" = ? ["contact_id", 1] rollback transaction
Rendered contacts/_form.html.erb (6.5ms)
Rendered contacts/edit.html.erb within layouts/application (28.7ms)
Completed 200 OK in 98ms (Views: 89.0ms | ActiveRecord: 0.7ms)

Started GET "/assets/jquery.js?body=1" for 127.0.0.1 at 2015-02-21 09:11:00 +0900

@tatsuyard
Copy link

DELETEから

@tatsuyard
Copy link

delete終わった
ブロックとprocについて調べる

@tatsuyard
Copy link

matchと==

@tatsuyard
Copy link

高度なコントローラスペック

から

@tatsuyard
Copy link

MK

@tatsuyard
Copy link

LGTM

👍 👎

@tatsuyard
Copy link

LGTM

@tatsuyard
Copy link

@tatsuyard
Copy link

@iberianpig
Copy link
Author

LGTM

👍 👎

LGTM

👍 👎

@tatsuyard
Copy link

mk

@tatsuyard
Copy link

LGTM

👍 👎

LGTM

👍 👎

LGTM

👍 👎

@iberianpig
Copy link
Author

LGTM

👍 👎

@iberianpig
Copy link
Author

LGTM

👍 👎

@tatsuyard
Copy link

LGTM

👍 👎

@tatsuyard
Copy link

LGTM

👍 👎

@iberianpig
Copy link
Author

puts "MKーーーッ!" if current_user.admin?

@iberianpig
Copy link
Author

# 君がMKだと叫びたい
it "should puts MK----!" do
  contact = create( :contact )
  STDOUT.should_receive(:puts).with("MKーーーッ!")
  get :show, id: contact
end

@iberianpig
Copy link
Author

      # :MKと叫びたい
      it "should puts MK----!" do
        contact = create( :contact )
        expect(STDOUT).to receive(:puts).with("MKーーーッ!")
        get :show, id: contact
      end

@iberianpig
Copy link
Author

    it "should not require login" do
      contact = create(:contact)
      get :show, id: contact
      expect(response).to render_template :show
    end
    # しかしMKと叫べない
    it "should NOT puts MK----!" do
      contact = create( :contact )
      expect(STDOUT).not_to receive(:puts).with("MKーーーッ!")
      get :show, id: contact
    end

@iberianpig
Copy link
Author

require 'rails_helper'
describe UsersController do

これが必要

@tatsuyard
Copy link

スペックファイルの先頭に何のコントローラかを明示すること↑↑

@tatsuyard
Copy link

コントローラスペックのクリーンアップ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants