Rails Tutorial: RSpec Decoupling

I am trying to do Exercise 2 of chapter 8.5 in the Michael Hartl Ruby on Rails Tutorial . The exercise is as follows:

Following the example in Section 8.3.3, review the specifications for user request and authentication (that is, the files currently in the spec / requests directory) and define the utility functions in spec / support / utilities.rb to separate the tests from the implementation. Extra credit. Organize the support code into separate files and modules and get everything to work by including the modules correctly in the auxiliary spec file.

Example 8.3.3: utilities.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 

The defined valid_signin(user) function is used in the next authentication_pages_spec.rb block and works fine.

 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 

So, in this example, I started creating my own valid_signup(user) name:

 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 

I use this block in user_pages_spec.rb as follows:

 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 

This does not work. Spork / Guard reports these errors:

 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 ' 

Errors seem to indicate that user.name in my valid_signup(user) function in utilities.rb not defined, but I see no reason. I restarted Guard several times and did rake db:test:prepare to make sure db testing (using postgresql) was ok.

Here is my factories.rb for completeness:

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

Before I try to separate more from the test suite, I would really like to solve this error and, more importantly, understand the reason for this.

EDIT

I tried your tips and edited the function in user_pages_spec.rb as follows:

 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 

Since I removed let(:user){FactoryGirl.create(:user)} from the function I guessed, the function no longer had a user, so I had to define valid_signup(user) , since the user variable for valid_signup bigger FactoryGirl didn't populate:

 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 

This did not work and gave me the following errors:

 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:in block (4 levels) in '

2) /: { _ ('title', text: user.name)} NoMethodError: undefined name' for nil:NilClass # ./spec/requests/user_pages_spec.rb:52:in (5 ) '

I also tried to run the test with valid_signup(user) in the way I used it before (with user.name, user.email, user.password, user.password_confirmation , which also did not work) with errors:

 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 ' 

Then I tried to run it without passing variables to user_pages_spec.rb : before { valid_signup() } and without a variable in a function 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 

This returns:

 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 ' 

Still no closer to the answer. I could have missed something simple. I don’t know what. I got what I did wrong at first: I just thought FactoryGirl was a way to create variables, and I did not know that it really did something with my test database.

+8
ruby-on-rails rspec
source share
4 answers

I will try to explain what happens in your initial test (which is easier for me to install 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 

Further information on using FactoryGirl: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#using-factories

+3
source share

FactoryGirl saves the user in the database, then you visit sign_in_path with the user already in the database and fill out 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 The girl saves the user in the database and fills out the form with an already accepted email.

EDIT:

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

You do not have a user-defined parameter since you deleted let(:user){FactoryGilr.create(:user)} and you must visit the correct 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 
+2
source share

I had the same problem and figured out a solution: when you define valid_signup, "page" should be selected as an argument. In the end, you are testing page elements, not the user.

specifications / 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 

<strong> specifications / queries / user_pages_spec.rb

describe "valid information" do until {valid_signup (page)}

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

Hope this helps!

UPDATE Now I understand that this works because of the size of the page variable (since this is the topic). To use "user", I added a line

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

higher

before { sign_up(user) } . Then it broke the later specification, where I also tried to use "user" as a variable, so I changed the name to "edituser". Here is a complete 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 

Hope this helps someone!

+2
source share

I was also interested to know about this, and I found an answer that could be more in line with what Hartle expected (although, as soon as training, I’m not 100% sure that the best answer is no more elegant or not).

Since we did not use FactoryGirl to register users, but instead signed them, I did not want to use it in my refactoring. This is what I have in my .rb utilities:

 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 

from

  describe "with valid information" do before { valid_signup } 

We do not need the user to be stored in the database in order to check one-time registration, as they do not need to go through several page views. In addition, since we are not looking for a user, we do not need an (user) argument after the valid_signup method (I think I have the correct terminology. Please correct me if I do not.)

0
source share

All Articles