0

Rails 4 アプリのテストに取り組んでおり、RSpec を使用するのは初めてです。標準の index、new、show、create... メソッドを持つ AppsController という名前のコントローラーがあり、それらはすべて Rails が提案する方法で動作します。「new」はオブジェクトの新しいインスタンスを作成し、create は実際にそれを保存し、show、show を表示しますそれとインデックスはすべてのオブジェクトを示しています。これが私の現在のテストです。潜在的な問題や改善できる点を誰でも見ることができますか?

FactoryGirl.define do
  factory :developer do
    email 'example@me.com'
    password 'new_york'
    password_confirmation 'new_york'
    tos '1'
  end

  factory :app do
    name 'New App'
    tos '1'
  end

  factory :invalid_app, parent: :app do
    name 'nil'
    tos '0'
  end
end

require 'spec_helper'

def create_valid!
  post :create, app: app_attributes
end

def create_invalid!
  post :create, app: app_invalid_attributes
end

def show!
  get :show, id: app
end

def update_valid!
  put :update, id: app, app: app_attributes
end

def update_invalid!
  put :update, id: app, app: app_invalid_attributes
end

def delete!
  delete :destroy, id: app
end

def http_success
  expect(response).to be_success
end

def expect_template(view)
  expect(response).to render_template(view)
end

describe AppsController do
  render_views

  before(:each) do
    @developer = FactoryGirl.create(:developer)
    @developer.confirm!
    sign_in @developer
  end

  let(:app) { FactoryGirl.create(:app, developer: @developer) }
  let(:app_attributes) { FactoryGirl.attributes_for(:app) }
  let(:app_invalid_attributes) { FactoryGirl.attributes_for(:invalid_app) }

  describe 'GET #index' do
    it 'responds with an HTTP 200 status' do
      get :index
      http_success
    end

    it 'renders the :index view' do
      get :index
      expect_template(:index)
    end

    it 'populates @apps with the current_developers apps' do
      app = FactoryGirl.create(:app, :developer => @developer)
      get :index
      expect(assigns(:app)).to eq([app])
    end
  end

  describe 'POST #create' do
    context 'with valid parameters' do
      it 'creates a new app' do
        expect { create_valid!
        }.to change(App, :count).by(1)
      end

      it 'redirects to the new app keys' do
        create_valid!
        expect(response).to redirect_to keys_app_path(App.last)
      end
    end

    context 'with invalid parameters' do
      it 'does not create the new app' do
        expect { create_invalid!
        }.to_not change(App, :count)
      end

      it 'renders the :new view' do
        create_invalid!
        expect_template(:new)
      end
    end
  end

  describe 'GET #show' do
    it 'responds with an HTTP 200 status' do
      show!
      http_success
    end

    it 'renders the :show view' do
      show!
      expect_template(:show)
    end

    it 'populates @app with the requested app' do
      show!
      expect(assigns(:app)).to eq(app)
    end
  end

  describe 'PUT #update' do
    context 'with valid parameters' do
      it 'locates the requested app' do
        update_valid!
        expect(assigns(:app)).to eq(app)
      end

      it 'changes app attributes' do
        update_valid!
        expect(app.name).to eq('Updated App')
      end

      it 'redirects to the updated app' do
        update_valid!
        expect(response).to redirect_to app
      end
    end

    context 'with invalid parameters' do
      it 'locates the requested app' do
        update_invalid!
        expect(assigns(:app)).to eq(app)
      end

      it 'does not change app attributes' do
        update_invalid!
        expect(app.name).to_not eq('Updated App')
      end

      it 'renders the :edit view' do
        update_invalid!
        expect_template(:edit)
      end
    end
  end

  describe 'DELETE #destroy' do
    it 'deletes the app' do
      expect { delete!
      }.to change(App, :count).by(-1)
    end

    it 'redirects to apps#index' do
      delete!
      expect(response).to redirect_to apps_url
    end
  end
end

count should have been changed by -1, but was changed by 0 - on DELETE #destroy

expecting <"new"> but rendering with <[]> - on POST #create

expected: "Updated App"
     got: "New App"     - on PUT #update

expecting <"edit"> but rendering with <[]> - on PUT #update

expected: [#<App id: nil, unique_id: "rOIc5p", developer_id: 18, name: "New App">]
     got: nil           - on GET #index
4

3 に答える 3

0

あなたのコードを読んで、私が考えたいくつかのこと:

引数を取らないメソッド呼び出しに括弧を含める必要はありません。ただhttp_success動作します。

最新の RSpec 期待値構文を一貫して使用するようにしてください。の代わりにassigns(:app).should eq(app)、 を使用しますexpect(assigns(:app)).to eq(app)。(これには例外が 1 つあります。これは、モック (つまり ) に対する期待です。これは、 RSpec 3should_receive(:message)の時点で最新の期待する構文のみを採用します。

コントローラーの仕様については、アクションを実際に呼び出すアクションごとに小さなメソッドを作成するのが好きです。仕様get :show, id: appで数回呼び出していることに気付くでしょう。GET #showスペックをもう少しドライアップするには、代わりにdescribeブロック内に次のメソッドを記述できます。

def show!
  get :show, id: app
end

1 つのハッシュ構文を一貫して使用するようにしてください。さらに、Rails 4 は Ruby 1.8 では実行できないため、hash-rocket Hash 構文を使用する理由は (ほとんど) ありません。

私が非常にうるさい場合は、通常、仕様内のインスタンス変数を臭いと見なします。ほとんどの場合、インスタンス変数はメモ化されたlet/givenブロックにリファクタリングする必要があります。

私が本当に、本当に、本当にうるさい場合は、あなたのようなコントローラーの仕様を、統合テストではなく、厳密にコントローラーの単体テストと考えることを好みます (それがカピバラの目的です)。モデルレイヤーをまったく実行していません。コントローラーがモデル層に正しいメッセージを送信していることのみをテストする必要があります。言い換えれば、すべてのモデル層のものをスタブ化する必要があります。例えば:

describe 'GET #show' do
  let(:app) { stub(:app) }

  before do
    App.stub(:find).and_return(app)
  end

  it 'populates @app' do
    get :show, id: app
    assigns(:app).should eq(app)
  end
end

これは個人的な好みであり、形而上学的な真実ではなく、必ずしも広く普及している標準的な慣習でさえないことを私は知っています. スペックを非常に高速に保ち、コントローラーのアクションが多すぎる場合に非常に明確なヒューリスティックを提供し、リファクタリングを検討する必要がある場合があるため、私はそれを好みます。身につけるのは良い習慣かもしれません。

于 2013-07-27T03:27:58.227 に答える
0

まず、確かではありませんが、invalid app工場が間違っているのではないかと思います。もしかして

  factory :invalid_app, parent: :app do
    name nil
    tos '0'
  end
  • nilNilClass文字列ではなくルビー"nil"として?

クリーンアップなどに関するその他のコメントについては、私の考えをいくつか紹介します。

beforeそれぞれにブロックを使用することで、一部のヘルパー メソッドと重複の必要性を回避できますdescribe。インデックステストだけを行うと、次のようなものになる可能性があります

describe 'GET #index' do
  before do
    get :index
  end
  it 'responds with an HTTP 200 status' do
    http_success
  end

  it 'renders the :index view' do
    expect_template(:index)
  end

  it 'populates @apps with the current_developers apps' do
    expect(assigns(:app)).to eq([app])
  end
end

app必要に応じて再作成しているため、再作成する必要がないことにも注意してくださいlet

失敗した場合、deleteカウントの変更が失敗している可能性があると思われます。これは、テスト フレームワークが (からlet) 新しいアプリを作成し、それを削除してカウントの変更が 0 になるためです。そのテストでは、確認する必要があります。あなたはappあなたの予想外に作られています。を使用しているためlet、次のようにすることができます。

describe 'DELETE #destroy' do
  it 'deletes the app' do
    # ensure that app is already created
    app
    expect { 
      delete!
    }.to change(App, :count).by(-1)
  end
end

または、スペックを実際に実行する前に作成を強制するletに変更します。let!

他の失敗に関しては、@ DanielWrightがヘルパーメソッドを提案したと思いましたが、それらはデバッグを複雑にしています。たとえば、アプリ名を「更新されたアプリ」に設定した場所がわかりません。おそらく、(その特定の) より明確なテストでは、ヘルパー メソッドを使用せず、より明示的にすることができます。何かのようなもの

describe 'PUT #update' do
  let(:app_attributes) { FactoryGirl.attributes_for(:app, name: 'The New App Name') }
  before do
    put :update, id: app, app: app_attributes
  end
  context 'with valid parameters' do
    it 'locates the requested app' do
      expect(assigns(:app)).to eq(app)
    end

    it 'changes app attributes' do
      # notice the reload which will make sure you refetch this from the db
      expect(app.reload.name).to eq('The New App Name')
    end

    it 'redirects to the updated app' do
      expect(response).to redirect_to app
    end
  end
end

他のエラーについては、コードのデバッグを開始することをお勧めします。それがうまくいくはずですか?出力ログを見ましたか?おそらく、テストがそこで機能し、コントローラーコードのエラーを見つけているのでしょう。ステップスルー デバッグを実行しましたか?

于 2015-01-07T09:18:29.283 に答える
0

小さなこと - #http_success メソッドがまったく同じことを 2 回テストしています。

#before ブロックの直後に #let ステートメントを配置して、 app への参照を除外することもできます。

let(:app) { FactoryGirl.create(:app, developer: @developer) }

次に、あなたの仕様では、ちょうど

it 'renders the :show view' do
  get :show, id: app
  expect_template(:show)
end

編集: 操作の順序は、1) @developer が #before ブロックで作成される、2) 仕様が入力される、3) 仕様での最初の参照appで、#let ブロックがインスタンスを作成する、のようになります。アプリ。

つまり、#index 仕様でアプリの作成を除外することはできません。その場合、仕様はアプリを作成する前にアクションを呼び出すためです。

于 2013-07-27T01:46:52.397 に答える