Rails Tutorial: RSpec test decoupling

大城市里の小女人 提交于 2019-12-04 03:31:20

I will try to explain what is going on in your original test (which I find easier to fix than the edited version):

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

More info on FactoryGirl usage: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#using-factories

FactoryGirl saves the user to the database, then you visit the sign_in_path with the user already on the database and fill the form for sign_in with valid_sigin(user)

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

When you do:

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

factory girl saves the user in the database, and you fill a form with an email already taken.

EDIT:

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

You dont have a variable user defined, since you deleted let(:user){FactoryGilr.create(:user)},and you should visit the right path, your current path is "sign_in_path" and should be "sign_up_path"

You should do something like this:

utilities.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
aceofbassgreg

I had the same problem and figured out the solution: when you define valid_signup, it should take 'page' as the argument. After all, you are testing the page elements, not the user.

spec/support/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

spec/requests/user_pages_spec.rb

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

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

I hope this helps!

UPDATE I realize now that this works because of the scope of the variable 'page' (since it's the subject). To use "user" I added the line

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

above

before { sign_up(user) }. This then broke a later spec where I also tried using 'user' as a variable, so I changed the name to 'editeduser'. Here's the full example:

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

Hopefully this helps someone!

I was curious about this one as well, and found an answer that may be more in line with what Hartl was expecting (though as just learning, I'm not 100% certain the top answer isn't more elegant or not).

Since we weren't using FactoryGirl to sign up users, but instead to sign them in, I didn't want to use it in my refactoring. This is what I have in my 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

and then in user_pages_spec.rb I replaced

  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

with

  describe "with valid information" do
  before { valid_signup } 

We don't need a user to be saved to the database just to check a one time sign up, since they don't need to persist through multiple page views. Also, since we don't look up a user, we don't need a (user) argument after valid_signup method (I think I have the terminology correct. Please correct me if I do not.)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!