8

Michael Hartl のRuby on Rails Tutorialの8.5 章の演習 2を実行しようとしています。演習は次のとおりです。

セクション 8.3.3 の例に従って、ユーザーおよび認証リクエストの仕様 (つまり、現在 spec/requests ディレクトリにあるファイル) を確認し、spec/support/utilities.rb でユーティリティ関数を定義して、テストを実装から分離します。追加クレジット: サポート コードを個別のファイルとモジュールに整理し、仕様ヘルパー ファイルにモジュールを適切に含めることですべてが機能するようにします。

例 8.3.3: utility.rb

include ApplicationHelper

def valid_signin(user)
  fill_in "Email",    with: user.email
  fill_in "Password", with: user.password
  click_button "Sign in"
end

RSpec::Matchers.define :have_error_message do |message|
  match do |page|
    page.should have_selector('div.alert.alert-error', text: message)
  end
end

定義されvalid_signin(user)た関数は次のブロックで使用され、正常にauthentication_pages_spec.rb動作します。

describe "with valid information" do
    let(:user){FactoryGirl.create(:user)}
    before { valid_signin(user) }

    it { should have_selector('title', text: user.name) }
    it { should have_link('Profile', href: user_path(user)) }
    it { should have_link('Sign out', href: signout_path) }
    it { should_not have_link('Sign in', href: signin_path) }

    describe "followed by signout" do
        before { click_link "Sign out" }
        it { should have_link('Sign in') }
    end
end

したがって、この例では、独自の名前付きの作成に取り掛かりましたvalid_signup(user):

def valid_signup(user)
    fill_in "Name",         with: user.name
    fill_in "Email",        with: user.email
    fill_in "Password",     with: user.password
    fill_in "Confirmation",         with: user.password_confirmation
end

このブロックを次のuser_pages_spec.rbように使用しています。

describe "with valid information" do
  let(:user){FactoryGirl.create(:user)}
  before { valid_signup(user) }

  it "should create a user" do
    expect { click_button submit }.to change(User, :count).by(1)
  end

  describe "after saving the user" do
    before { click_button submit }
    let(:user) { User.find_by_email(user.email) }

    it { should have_selector('title', text: user.name) }
    it { should have_selector('div.alert.alert-success', text: 'Welcome') }
    it { should have_link('Sign out') }
  end
end

うまくいきません。Spor/Guard は次のエラーを報告します。

Failures:

  1) UserPages signup with valid information should create a user
     Failure/Error: expect { click_button submit }.to change(User, :count).by(1)
       count should have been changed by 1, but was changed by 0
     # ./spec/requests/user_pages_spec.rb:46:in `block (4 levels) in '

  2) UserPages signup with valid information after saving the user 
     Failure/Error: before { valid_signup(user) }
     NoMethodError:
       undefined method `name' for nil:NilClass
     # ./spec/support/utilities.rb:10:in `valid_signup'
     # ./spec/requests/user_pages_spec.rb:43:in `block (4 levels) in '

  3) UserPages signup with valid information after saving the user 
     Failure/Error: before { valid_signup(user) }
     NoMethodError:
       undefined method `name' for nil:NilClass
     # ./spec/support/utilities.rb:10:in `valid_signup'
     # ./spec/requests/user_pages_spec.rb:43:in `block (4 levels) in '

  4) UserPages signup with valid information after saving the user 
     Failure/Error: before { valid_signup(user) }
     NoMethodError:
       undefined method `name' for nil:NilClass
     # ./spec/support/utilities.rb:10:in `valid_signup'
     # ./spec/requests/user_pages_spec.rb:43:in `block (4 levels) in '

valid_signup(user)エラーは、私の関数の user.name がutilities.rb定義されていないことを示唆しているようですが、その理由はわかりません。Guard を数回再起動rake db:test:prepareし、テスト データベース (postgresql を使用) が正常であることを確認しました。

これが完全を期すための私のfactories.rbものです:

FactoryGirl.define do
    factory :user do
        name    "Example User"
        email   "user@example.com"
        password    "foobar"
        password_confirmation   "foobar"
    end
end

テスト スイートをさらに分離しようとする前に、このエラーを解決し、さらに重要なこととして、その理由を理解したいと思います。

編集

私はあなたのヒントを試し、関数user_pages_spec.rbを次のように編集しました:

describe "with valid information" do
      before { valid_signup(user) }

      it "should create a user" do
        expect { click_button submit }.to change(User, :count).by(1)
      end

      describe "after saving the user" do
        before { click_button submit }
        let(:user) { User.find_by_email('user@example.com') }

        it { should have_selector('title', text: user.name) }
        it { should have_selector('div.alert.alert-success', text: 'Welcome') }
        it { should have_link('Sign out') }
      end
    end

関数から削除let(:user){FactoryGirl.create(:user)}したので、関数で作成されたユーザーがなくなったと推測したため、変数が FactoryGirl によって埋められなくなっvalid_signup(user)たように定義する必要がありました。uservalid_signup

def valid_signup(user)
    fill_in "Name",     with: "Example User"
    fill_in "Email",    with: "user@example.com"
    fill_in "Password", with: "foobar"
    fill_in "Confirmation", with: "foobar"
end

これは機能せず、次のエラーが発生しました。

Failures:

1) UserPages signup with valid information should create a user Failure/Error: before { valid_signup(user) } NameError: undefined local variable or method user' for #<RSpec::Core::ExampleGroup::Nested_5::Nested_3::Nested_2:0x007fdafc5088c0> # ./spec/requests/user_pages_spec.rb:42:inblock (4 levels) in '

2) UserPages signup with valid information after saving the user Failure/Error: it { should have_selector('title', text: user.name) } NoMethodError: undefined method name' for nil:NilClass # ./spec/requests/user_pages_spec.rb:52:inblock (5 levels) in '

valid_signup(user)また、以前と同じ方法でテストを実行しようとしました( user.name, user.email, user.password, user.password_confirmation、どちらも機能せず、エラーが発生しました:

Failures:

  1) UserPages signup with valid information should create a user
     Failure/Error: before { valid_signup(user) }
     NameError:
       undefined local variable or method `user' for #
     # ./spec/requests/user_pages_spec.rb:42:in `block (4 levels) in '

  2) UserPages signup with valid information after saving the user 
     Failure/Error: it { should have_selector('title', text: user.name) }
     NoMethodError:
       undefined method `name' for nil:NilClass
     # ./spec/requests/user_pages_spec.rb:52:in `block (5 levels) in '

次に、変数をuser_pages_spec.rb:に渡さbefore { valid_signup() }ず、関数 in に変数を渡さずに実行してみましたutilities.rb:

def valid_signup()
    fill_in "Name",     with: "Example User"
    fill_in "Email",    with: "user@example.com"
    fill_in "Password", with: "foobar"
    fill_in "Confirmation", with: "foobar"
end

これは以下を返しました:

Failures:

  1) UserPages signup with valid information should create a user
     Failure/Error: before { valid_signup(user) }
     NameError:
       undefined local variable or method `user' for #
     # ./spec/requests/user_pages_spec.rb:42:in `block (4 levels) in '

  2) UserPages signup with valid information after saving the user 
     Failure/Error: it { should have_selector('title', text: user.name) }
     NoMethodError:
       undefined method `name' for nil:NilClass
     # ./spec/requests/user_pages_spec.rb:52:in `block (5 levels) in '

まだ答えには近づいていません。私は簡単なことを見落としているかもしれません。何の手がかりもありません。しかし、私が最初にしたことは間違っていました。FactoryGirl は変数を作成する方法だと思っただけで、実際にテスト データベースに何かを行うことを知りませんでした。

4

4 に答える 4

3

元のテストで何が起こっているかを説明しようとします(編集したバージョンよりも修正しやすいと思います):

describe "with valid information" do
  let(:user) {FactoryGirl.build(:user)} # FactoryGirl.create will save the instance, you should be using build instead
  before { valid_signup(user) }

  it "should create a user" do
    expect { click_button submit }.to change(User, :count).by(1)
  end

  describe "after saving the user" do
    before { click_button submit }
    # let(:user) { User.find_by_email(user.email) } # this is not needed any more 

    it { should have_selector('title', text: user.name) }
    it { should have_selector('div.alert.alert-success', text: 'Welcome') }
    it { should have_link('Sign out') }
  end
end

FactoryGirl の使用方法の詳細: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#using-factories

于 2012-11-29T12:26:33.370 に答える
2

FactoryGirl はユーザーをデータベースに保存します。次に、データベースに既に存在するユーザーで sign_in_path にアクセスし、sign_in のフォームに valid_sigin(user) を入力します。

let(:user){FactoryGirl.create(:user)}
before { valid_signin(user) }

あなたがするとき:

let(:user){FactoryGirl.create(:user)}
before { valid_signup(user) }

factory girl はユーザーをデータベースに保存し、既に取得したメールをフォームに入力します。

編集:

  describe "with valid information" do
  before { valid_signup(user) }

を削除したため、変数ユーザーが定義されていませんlet(:user){FactoryGilr.create(:user)}。正しいパスにアクセスする必要があります。現在のパスは「sign_in_path」であり、「sign_up_path」である必要があります。

次のようにする必要があります。

ユーティリティ.rb

def valid_sign_up(user)
  fill_in "Name",         with: user.name
  fill_in "Email",        with: user.email
  fill_in "Password",     with: user.password
  fill_in "Confirmation", with: user.password_confirmation
end

user_pages_spec.rb

describe "with valid information" do
  let(:user){User.new(name: "my name", email: "myemail@example"...)
  before do        
    visit sign_up
    valid_sign_up(user)
  end 

  it "should create a user" do
    expect { click_button submit }.to change(User, :count).by(1)
  end
end
于 2012-11-28T14:49:48.297 に答える
2

私は同じ問題を抱えており、解決策を見つけました。valid_signup を定義するときは、引数として「ページ」を使用する必要があります。結局のところ、ユーザーではなく、ページ要素をテストしています。

仕様/サポート/utilities.rb

def valid_signup(page)
  fill_in "Name", with: "Example User"
  fill_in "Email",          with: "user@example.com"
  fill_in "Password",       with: "foobar"
  fill_in "Confirmation",   with: "foobar"
end

仕様/リクエスト/user_pages_spec.rb

{ valid_signup(page) } の前に「有効な情報で」記述する

  it "should create a user" do
    expect { click_button submit }.to change(User, :count).by(1)
  end

これが役立つことを願っています!

更新変数「ページ」のスコープのためにこれが機能することがわかりました(それがサブジェクトであるため)。「ユーザー」を使用するには、次の行を追加しました

let(:user) { FactoryGirl.create(:user) }

その上

前に{ sign_up(user) }。これにより、変数として「user」を使用しようとした後の仕様が壊れたため、名前を「editeduser」に変更しました。完全な例は次のとおりです。

user_pages_spec.rb

require 'spec_helper'

describe "UserPages" do

  subject { page }

...

  describe "signup page" do
    before { visit signup_path }

    let(:submit) { "Create my account" }

    it { should have_selector('h1', text: 'Sign up') }
    it { should have_selector('title', text: full_title('Sign up')) }

  describe "with invalid information" do
    it "should not create a user" do
    expect { click_button submit }.not_to change(User, :count)
  end

  describe "after submission" do
    before { click_button submit }

    it { should have_selector('title', text: 'Sign up') }
    it { should have_content('error') }
  end
end

  describe "with valid information" do
    let(:user) { FactoryGirl.create(:user) }
    before { sign_up(user) }

    it "should create a user" do
      expect { click_button submit }.to change(User, :count).by(1)
    end

    describe "after saving the user" do
      before { click_button submit }
      let(:editeduser) { User.find_by_email('user@example.com') }

      it { should have_selector('title', text: editeduser.name) }
      it { should have_selector('div.alert.alert-success', text: 'Welcome') }
      it { should have_link('Sign out') }
    end
  end
end

うまくいけば、これは誰かを助けます!

于 2013-01-30T03:11:33.937 に答える
0

私もこれに興味があり、Hartlが期待していたものとより一致する可能性のある答えを見つけました(ただし、学習しているだけで、トップの答えがよりエレガントであるかどうかは100%確信できません)。

FactoryGirlを使用してユーザーをサインアップするのではなく、ユーザーをサインインするために、リファクタリングで使用したくありませんでした。これは私が私のutilities.rbに持っているものです:

def valid_signup
  fill_in "Name",         with: "Example User"
  fill_in "Email",        with: "user@example.com"
  fill_in "Password",     with: "foobar"
  fill_in "Confirmation", with: "foobar"
end

そしてuser_pages_spec.rbで私は置き換えました

  describe "with valid information" do
  before do
    fill_in "Name",           with: "Example User"
    fill_in "Email",          with: "user@example.com"
    fill_in "Password",       with: "foobar"
    fill_in "Confirmation",   with: "foobar"
  end

  describe "with valid information" do
  before { valid_signup } 

ユーザーは複数のページビューを介して永続化する必要がないため、1回のサインアップを確認するためだけにユーザーをデータベースに保存する必要はありません。また、ユーザーを検索しないため、valid_signupメソッドの後に(user)引数は必要ありません(用語は正しいと思います。正しくない場合は修正してください)。

于 2012-12-24T18:15:37.963 に答える