How to create Page Objects classes for Rspec Features - ruby-on-rails

I have been trying, unsuccessfully, refactor some rspec features using Page Objects, but I'm having a hard time with it.
I'm not a experienced RoR developers. Probably this is a basic error.
I have a spec feature like this:
require 'capybara/rspec'
feature 'Acessing the dashboard' do
scenario 'User see the dashboard after login' do
sign_in 'teste','123'
expect(page).to have_css 'h1', text: 'Dashboard'
end
def sign_in(login, password)
visit '/'
fill_in 'login', :with => login
fill_in 'password', :with => password
click_button 'Autenticar'
end
end
But, I would like to share my sign_in method with another features through a Login Page Object like this:
class LoginPage
include Capybara::DSL
def sign_in(login, password)
visit '/'
fill_in 'login', :with => login
fill_in 'password', :with => password
click_button 'Authenticate'
end
end
So, I changed my feature:
feature 'Acessing the dashboard' do
let(:login_page) { LoginPage.new }
scenario 'User see the dashboard after login' do
login_page.sign_in 'teste','123'
expect(page).to have_css 'h1', text: 'Dashboard'
end
end
But I receive the following error:
Failure/Error: let(:login_page) { LoginPage.new }
NameError: uninitialized constant LoginPage
I really don't know what to do.
I created the LoginPage class in /spec/support/LoginPage.rb. Is it correct? Should I put this class in other path?
Somebody can help me to deal with it?
Thanks a lot.

You have to require your support folder files in you spec_helper.rb file
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }

Related

How to factor Capybara rspec testing code?

I need to test a system in which everything is available only after a user is signed in using Devise. Every time I use "it" I have to include the signup code.
Is there a way to factor the code below so that the "let's me make a new post" test and similar tests won't have to include the sign up?
describe "new post process" do
before :all do
#user = FactoryGirl.create(:user)
#post = FactoryGirl.create(:post)
end
it "signs me in" do
visit '/users/sign_in'
within(".new_user") do
fill_in 'Email', :with => 'user#example.com'
fill_in 'Password', :with => 'password'
end
click_button 'Log in'
expect(page).to have_content 'Signed in successfully'
end
it "let's me make a new post" do
visit '/users/sign_in'
within(".new_user") do
fill_in 'Email', :with => 'user#example.com'
fill_in 'Password', :with => 'password'
end
click_button 'Log in'
visit '/posts/new'
expect( find(:css, 'select#post_id').value ).to eq('1')
end
end
Your first option is to use the Warden methods provided, as per the documentation on this page:
https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
Your second option is just to login for real in your tests as you have done in your examples. You can streamline this though by creating some helper methods to do the login work rather than duplicating the code in all of your tests.
To do this, I would create a support directory within your spec directory, and then a macros directory within that. Then create a file spec/support/macros/authentication_macros.rb:
module AuthenticationMacros
def login_as(user)
visit '/users/sign_in'
within('.new_user') do
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
end
click_button 'Log in'
end
end
Next, update your RSpec config to load your macros. In either spec_helper.rb or rails_helper.rb if you're using a newer setup:
# Load your support files
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# Include the functions defined in your modules so RSpec can access them
RSpec.configure do |config|
config.include(AuthenticationMacros)
end
Finally, update your tests to use your login_as function:
describe "new post process" do
before :each do
#user = FactoryGirl.create(:user)
#post = FactoryGirl.create(:post)
login_as #user
end
it "signs me in" do
expect(page).to have_content 'Signed in successfully'
end
it "let's me make a new post" do
expect( find(:css, 'select#post_id').value ).to eq('1')
end
end
Obviously, make sure you have password defined in your user factory.

login test with rspec + Capybara not redirecting to home page

I'm trying requests tests with Capybara and it doesnt seem to work. This is my test file:
describe "Sessions", :type => :request do
let(:company) { FactoryGirl.create(:company) }
let(:user) { FactoryGirl.create(:admin, company_id: company.id ) }
describe "login page" do
it "signs me in" do
visit '/users/sign_in'
within("#new_user") do
fill_in 'Email', :with => user.email
fill_in 'Password', :with => user.password
end
click_button 'Sign in'
expect(page).to have_content 'Agenda'
end
end
end
Throws the following Error:
Failure/Error: expect(page).to have_content 'Agenda'
expected #has_content?("Agenda") to return true, got false
Im not sure if the problem is at logging in or at redirecting. But if i change the last line in the test for this:
expect(page).to have_content 'Invalid email'
I get the same Error.
Thanks in advance.
UPDATE:
i'm using devise for login
Simplest thing is to use screenshot_and_save_page to take a screenshot of the page just before the failing test, maybe you can determine what the problem is there.

Rspec with Rails - url_helpers outside before/it blocks?

In my app, I have an identical form on two separate pages. I've written a series of tests for the one page, and would like to simply loop over the test suite for my second page, passing in the page for which the test suite should run against. The problem is, I can't seem to access the url_helpers in a clean way.
This is what I have so far that works:
require 'rails_helper'
describe "my tests" do
subject { page }
paths = ["/signin", "/signup"]
paths.each do |path|
describe "user signs in via #{path}" do
before { visit path }
describe "user enters invalid information" do
before do
fill_in "Email", with: user.email, match: :first
fill_in "Password", with: user.password.reverse, match: :first
click_button "Sign in"
end
it { should have_title("Sign in") }
it { should have_content "Invalid email/password combination!" }
its(:current_path) { should eq "/signin" }
end
end
end
If I replace "/signin" with signin_path, I get
undefined local variable or method `signin_path' for RSpec::ExampleGroups::Authentication:Class (NameError)
But I'm using url_helpers successfully throughout the rest of my test script, albeit inside before and it blocks. If I fully qualify the url_helper with Rails.application.routes.url_helpers.signin_path it works, but I'd rather have this work automatically. If I do an include at the top of the script, I get
ActionView::Template::Error:
arguments passed to url_for can't be handled. Please require routes or provide your own implementation
Again, not ideal. I'd rather have this just work. Any thoughts?
Thanks in advance.
Are there no errors if you just did one path? If so, then why not use a RSpec shared_example
require 'rails_helper'
shared_examples 'get_user_in' do |path|
describe "user signs in via #{path}" do
before { visit path }
describe "user enters invalid information" do
before do
fill_in "Email", with: user.email, match: :first
fill_in "Password", with: user.password.reverse, match: :first
click_button "Sign in"
end
it { should have_title("Sign in") }
it { should have_content "Invalid email/password combination!" }
its(:current_path) { should eq "/signin" }
end
end
end
describe "/signin" do
subject { page }
include_examples "get_user_in", 'signin'
end
describe "/signup" do
subject { page }
include_examples "get_user_in", 'signup'
end
Didn't test this out, so there might be some error here.

Trouble with refactoring code for rspec feature tests in rails 4

I am trying to give the user of my web app the ability to login with a password. I am rolling my own authentication instead of using a gem. I read this article about refactoring Rspec/Capybara tests:
http://robots.thoughtbot.com/rspec-integration-tests-with-capybara
I liked what I read and decided to give refactoring a try. I created a session helper file for my feature tests.
module Features
module SessionHelpers
def sign_in
user = create(:user)
visit '/authentications/new'
fill_in 'Login', with: user.name
fill_in 'Password', with: user.password
click_button 'Sign in'
end
end
end
I then called the sign_in function in my login tests. Here is a little sample.
require 'spec_helper'
feature "signing in" do
before :each do
User.create(:name => 'user#example.com', :password => 'caplin')
end
scenario "user who logs in with correct credentials" do
sign_in
expect(page).to have_content 'Hi user#example.com'
end
end
Unfortunately, I keep getting this error message:
2) signing in user who logs in with correct credentials
Failure/Error: sign_in
NoMethodError:
undefined method `create' for #<RSpec::Core::ExampleGroup::Nested_3:0x007ffc85012438>
# ./spec/support/features/session_helpers.rb:4:in `sign_in'
# ./spec/features/user_logs_in_spec.rb:13:in `block (2 levels) in <top (required)>'
Basically, I need some way to grab the user I created and pass it into the sign_in function. Any hints?
I'm guessing your first issue is a different test configuration than the one the ThoughBot example has. create is not to my knowledge a default method available in RSpec; I'm going to guess they've added every FactoryGirl method to the testing scope. If you're using FactoryGirl, you can get the same behavior by just namespacing the create command:
def sign_in
user = FactoryGirl.create(:user)
visit '/authentications/new'
fill_in 'Login', with: user.name
fill_in 'Password', with: user.password
click_button 'Sign in'
end
However, this won't quite get you everything that you asked for, since you still won't be able to add a custom user. An easy way for this would allow for a user to be passed in:
def sign_in(user=nil)
user ||= FactoryGirl.create(:user)
...
end
This will create the user for you if you don't pass one in on the sign_in call.
Going back to the spec you posted, you'd want to change it to this:
feature "signing in" do
before :each do
#user = User.create(:name => 'user#example.com', :password => 'caplin')
end
scenario "user who logs in with correct credentials" do
sign_in(#user)
expect(page).to have_content 'Hi user#example.com'
end
end
You'd need to attach the user you created to a variable (#user), then pass it to the sign_in as needed.
Problem in you model
module Features
module SessionHelpers
def sign_in
user = create(:user) # <- this method allow only in FactoryGirl
visit '/authentications/new'
fill_in 'Login', with: user.name
fill_in 'Password', with: user.password
click_button 'Sign in'
end
end
end
i use another way. Create a class and include FactroyGirl methods and Capybara::DSL like this
class Features
include FactoryGirl::Syntax::Methods
include Capybara::DSL
def sign_in
user = create(:user) #<- FactroyGirl
visit '/authentications/new' #<- Capybara
fill_in 'Login', with: user.name #<- Capybara
fill_in 'Password', with: user.password #<- Capybara
click_button 'Sign in' #<- Capybara
self #<- return page
end
end
in spec
feature "signing in" do
let(:login_user) { Features.new }
scenario "user who logs in with correct credentials" do
page = login_user.sign_in
expect(page).to have_content 'Hi user#example.com'
end
end
You can accomplish this by including FactoryGirl in your tests. Your RSpec configuration block (in spec_helper.rb or in the new version of RSpec rails_helper.rb) should look like this:
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end

Where/how to include helper methods for capybara integration tests

I"m using capybara for my integration/acceptance tests. They're in /spec/requests/ folder. Now I have a few helper methods that I use during acceptance tests. One example is register_user which looks like this
def register_user(user)
visit home_page
fill_in 'user_name', :with => user.username
fill_in 'password', :with => user.password
click_button 'sign_up_button'
end
I want to use this method in several different acceptance tests (they're in different files). What's the best way to include this? I've tried putting it in spec/support/ but it hasn't been working for me. After spending some time on it I realized I don't even know if it's a good way of doing it so I figured I'd ask here.
Note: I am using rails 3, spork and rspec.
Put your helper to the spec/support folder and do something like this:
spec/support/:
module YourHelper
def register_user(user)
visit home_page
fill_in 'user_name', :with => user.username
fill_in 'password', :with => user.password
click_button 'sign_up_button'
end
end
RSpec.configure do |config|
config.include YourHelper, :type => :request
end
I used the given solution by #VasiliyErmolovich, but I changed the type to make it work:
config.include YourHelper, :type => :feature
Explicit way with ruby
Using include:
# spec/support/your_helper.rb
class YourHelper
def register_user(user)
visit home_page
fill_in 'user_name', :with => user.username
fill_in 'password', :with => user.password
click_button 'sign_up_button'
end
end
describe MyRegistration do
include YourHelper
it 'registers an user' do
expect(register_user(user)).to be_truthy
end
end

Resources