RSpec - testing multiple expectations across routes - ruby

I'm attempting to use shared_examples as a way to repeat expectations across multiple routes. In particular, I want to test whether some static assets in my header and footer are loading. However, I get an error saying that:
RSpec::Core::ExampleGroup::WrongScopeError: `it_behaves_like` is not available from within an example (e.g. an `it` block) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc). It is only available on an example group (e.g. a `describe` or `context` block).
Now, I'm not sure how to remedy this. This is my current setup.
shared_examples_for 'a page' do
describe 'should load static assets' do
it 'header, footer and icons' do
expect(page).to have_css 'footer.footer'
expect(page).to have_css '#navbar-brand'
brand = page.first(:css, '#navbar-brand')
visit brand[:src]
expect(page.status_code).to be 200
end
end
end
describe 'site pages should load static assets on requests', { :type => :feature } do
after :all do
it_behaves_like 'a page'
end
it 'home page' do
visit '/'
expect(page).to have_css 'div#main-menu a', count: 5
page.all('link[rel~="icon"]').each do |fav|
visit fav[:href]
page.status_code.should be 200
end
expect(page).to have_css 'div#main-menu'
...
end
it 'about page should have icons, footer and a header' do
visit '/about'
...
end
end
Another attempt was this:
describe 'home page' do
it_behaves_like 'a page'
end
Both fail for the same reason above. So, if I want to check the same things on every page, what is a better solution?

In RSpec 3 this should work
require 'spec_helper'
shared_examples 'a page' do
it 'has a header' do
expect(page).to have_css 'footer.footer'
end
it 'has a brand' do
expect(page).to have_css '#navbar-brand'
end
it 'points out to brand page' do
brand = page.first(:css, '#navbar-brand')
visit brand[:src]
expect(page.status_code).to be 200
end
end
describe 'home page' do
before { visit '/' }
it_behaves_like 'a page'
it 'does something else' do
# ...
end
end
Alternatively you could use a block
describe 'home page' do
it_behaves_like 'a page' do
before { visit '/' }
end
end

Related

How to get a detailed error message in Capybara unit tests

How do I get a detailed error message in Capybara unit tests?
describe "About" do
it "should have the h1 'About Us'" do
visit '/static_pages/about'
page.should have_selector('h1',
:text => "About Us")
end
it "should have the title 'About'" do
visit '/static_pages/about'
page.should have_title("About")
end
This tests for title to be "About".
How do I add a custom error message like:
Expected "About" but found "ABT". Please Rectify the mistake.
You can add a custom error message described in "Customized message" like this:
it "should have the title 'About'" do
visit '/static_pages/about'
expect(page).to have_title("About"), lambda { "Expected 'About' but found '#{page.first("title", visible: false).native.text}'. Please Rectify the mistake."}
end
You can add custom error messages as shown below, also you should add to take screenshot to debug the issue.
describe "About" do
it "should have the h1 'About Us'" do
visit '/static_pages/about'
page.should have_selector('h1',
:text => "About Us")
end
it "should have the title 'About'" do
visit '/static_pages/about'
textToSearch="About"
begin
page.should have_title("#{textToSearch}")
rescue Exception => e
puts "Expected '#{textToSearch}' but found '#{page.first("title", visible: false).native.text}'. Please Rectify the mistake."
randomNumber=rand(100000)
page.save_screenshot("abc-#{randomNumber}.png",:full=>true)
raise e
end
end
Hope this will Helps :)

How can I use pageobject navigation routes when I have multiple methods with parameters?

Here, I have two methods in a class with one parameter each and I want to use routes advantage. How can I pass parameters if I am using cucumber I mean how can I pass parameters from step definitions to method if I use like this:
feature:
Feature: Github Test Case
Background:
Given I am on githubpage
Scenario Outline: I should see one of the repositories
When I click on "<user>" and select "<repo>" link
Then I should see "Information Technology Association website repo"
Examples:
| user | repo |
| sample | sample_repo |
step def:
Given(/^I am on githubpage$/) do
visit(LoginPage).do_login
end
Then(/^I should see "([^"]*)"$/) do |message|
#current_page.text.should include message
end
When(/^I click on "([^"]*)" and select "([^"]*)" link$/) do |user, repo|
# currently using like this
navigate_to(GithubPage).click_on(user)
navigate_to(GithubPage).select_repo(repo)
# but i need like this
navigate_to(GithubPage).select_repo
# or
navigate_all
end
class:
class GithubPage
include PageObject
link(:repo, text: /Repositories/)
def click_on(user)
span_element(text: "#{user}", index: 1).click
repo_element.click
end
def select_repo(repo)
link_element(xpath: "//a[contains(text(),'#{repo}')]").when_present.click
end
end
routes:
PageObject::PageFactory.routes = {
:default => [[GithubPage, :click_on], [GithubPage, :select_repo]]
}
Here's an example from PageObject::PageFactory where Cheezy passes an argument to a method as part of his routes definition:
PageObject::PageFactory.routes = {
:default => [[PageOne,:method1], [PageTwoA,:method2], [PageThree,:method3]],
:another_route => [[PageOne,:method1, "arg1"], [PageTwoB,:method2b], [PageThree,:method3]]
}
The problem is, of course, that you don't have that argument at the time these routes are defined. You need to load them dynamically. Something like this might work, but I haven't tested it:
When /^I click on "([^"]*)" and select "([^"]*)" link$/ do |user, repo|
PageObject::PageFactory.routes[:default].map do |route|
route << user if route[1] == :click_on
route << repo if route[1] == :select_repo
end
navigate_all
end
But if you're going to go to all that trouble, you're better off passing a block to PageObject::PageFactory#on:
When /^I click on "([^"]*)" and select "([^"]*)" link$/ do |user, repo|
on GithubPage do |page|
page.click_on user
page.select_repo repo
end
end

Rspec testing if text is pluralized (Hartl 10.5 Exercise 1)

Ruby 1.9.3, Rails 3.2.2
I'm trying to write an Rpsec (Capybara) to test if my page is properly pluralizing the word "Post". I get an error when running my Rspec that states:
c:/.../my_app/spec/requests/user_pages_spec.rb:58: in `block (3 levels) in ': undefined local variable or method 'user' for ... (NameError)
Here's the relevant test:
describe "profile page" do
let(:user){FactoryGirl.create(:user)}
let!(:m1){FactoryGirl.create(:micropost, user: user, content: "Food")}
let!(:m2){FactoryGirl.create(:micropost, user: user, content: "Bar")}
before {visit user_path(user)}
it {should have_selector('h1', text: user.name)}
it {should have_selector('title', text: user.name)}
describe "pagination" do
before(:all){40.times {FactoryGirl.create(:micropost, user: user, content: "Food")}}
after(:all){User.delete_all}
it {should have_selector('div.pagination')}
end
describe "microposts" do
it {should have_content(m1.content)}
it {should have_content(m2.content)}
it {should have_content(user.microposts.count)}
before do
sign_in user
visit root_path
User.delete_all
end
it "should pluralize post numbers" do
FactoryGirl.create(:micropost, user: user, content: "Food")
page.should have_content("1 micropost")
2.times {FactoryGirl.create(:micropost, user: user, content: "Food")}
page.should have_content("2 microposts")
end
I'm not sure if I'm going about testing for pluralization of posts the right way either, but I'm mainly stumped about why that block can't "see" the users object since the line right after my if statement can see it. if I comment out the if block everything runs fine.
Any code outside of it doesn't have access to the variables defined with before, let, etc. You need to place it inside an it call, say with a text argument that describes the total if/else test. Could you update this question to include the other code from your spec that's in scope?
Profile page doesn't have pluralization to test.
The test should be for static_pages#home.
describe 'Home page' do
describe 'for signed-in users' do
let(:user) { FactoryGirl.create(:user) }
before do
FactoryGirl.create(:micropost, user: user, content: 'Lorem ipsum')
FactoryGirl.create(:micropost, user: user, content: 'Dolor sit amet')
sign_in user
visit root_path
end
it { should have_content('micropost'.pluralize(user.microposts.count)) }
end
end

how to run capybara commands once, then run some tests

I have the given testing code:
describe 'A new user', js: true do
before do
#new_user = Fabricate.build(:user)
end
it 'should sign up' do
#login code
visit '/'
click_link 'Login'
fill_in 'user[email]', :with => #new_user.email
fill_in 'user[password]', :with => #new_user.password
click_button 'Login now'
#login code end
page.should have_content("Hello #{#new_user.first_name}!")
current_path.should == dashboard_path
end
it 'should receive a confirmation mail' do
#same login code again
visit '/'
click_link 'Login'
fill_in 'user[email]', :with => #new_user.email
fill_in 'user[password]', :with => #new_user.password
click_button 'Login now'
mail = ActionMailer::Base.deliveries.last
assert_equal #new_user.email, mail['to'].to_s
end
end
Now I want to add more tests.
To avoid code doubling, how can I run the capybara login code once before all tests?
One solution would be to put the login code in the before method. Another would be to create a method do_login, put the code in it and run every test like this:
it 'should do something after login' do
do_login
#test code here
end
But for both solutions, the code is run for every test and thats not what I want. Putting the login code in a before(:all) doesn't work, too.
How can I run some capybara code once and then do all the tests after this?
You can't run capybara code once and then run all the tests. You always start from scratch. Your proposed solution with before(:each) or helper method is the only posibility. (It's possible to run some ruby before(:all) e.g. create objects outside the transaction check here but not Capybara)
To speed up your specs you can test login feature in separate spec and then somehow stub the authentication but it depends on your implementation.
If you are using Devise check devise wiki: https://github.com/plataformatec/devise/wiki/How-To:-Controllers-tests-with-Rails-3-(and-rspec)

Rspec fails when let(:base_title) added while refactoring

I am following Ruby on rails tutorial by Michael Hartl.
Refactoring a simple test using rspec is failing it, I will paste first what works, then i will paste what works ....
require 'spec_helper'
describe "Static pages" do
describe "Home page" do
it "should have the h1 'Sample App'" do
visit '/static_pages/home'
page.should have_selector('h1', :text => 'Sample App')
end
it "should have the title 'Home'" do
visit '/static_pages/home'
page.should have_selector('title',
:text => "Ruby on Rails Tutorial Sample App | Home")
end
end
describe "Help page" do
it "should have the h1 'Help'" do
visit '/static_pages/help'
page.should have_selector('h1', :text => 'Help')
end
it "should have the title 'Help'" do
visit '/static_pages/help'
page.should have_selector('title',
:text => "Ruby on Rails Tutorial Sample App | Help")
end
end
end
Now I will paste what fails in rspec
require 'spec_helper'
describe "Static pages" do
let(:base_title) { "Ruby on Rails Tutorial Sample App |" }
describe "Home page" do
it "should have the h1 'Sample App'" do
visit '/static_pages/home'
page.should have_selector('h1', :text => 'Sample App')
end
it "should have the title 'Home'" do
visit '/static_pages/home'
page.should have_selector('title', :text => "#{base_title} Home")
end
end
describe "Help page" do
it "should have the h1 'Help'" do
visit '/static_pages/help'
page.should have_selector('h1', :text => 'Help')
end
it "should have the title 'Help'" do
visit '/static_pages/help'
page.should have_selector('title', :text => "#{base_title} Help")
end
end
end
Also i am new to this so i would like to ask what more information might be required ... the change is obviously
let(:base_title) { "Ruby on Rails Tutorial Sample App |" }
Thanks for any help !
Failing error being ....
Failures:
1) Static pages Home page should have the title 'Home'
Failure/Error: page.should have_selector('title', :text => "#base_title} Home")
expected css "title" with text "#base_title} Home" to return something
# ./spec/requests/static_pages_spec.rb:16:in `block (3 levels) in '
2) Static pages Help page should have the title 'Help'
Failure/Error: page.should have_selector('title', :text => "#{base_title} Help")
expected css "title" with text "Ruby on Rails Tutorial Sample App | Help" to return something
# ./spec/requests/static_pages_spec.rb:29:in `block (3 levels) in '
It has been a while since this question was asked. If it's still relevant:
There are two spaces after #{base_title}.
Delete a space and it should work.
it looks like you missed a { in the first example:
"title" with text "#base_title} Home"
this string is just not interpolated and has nothing to do with let
and other error should not occur if your code and test work properly.
Just be ahead of changing Capybara and Webrat syntaxes. This should work for your example:
page.should have_selector('h1', text: "#{base_title} Help")
or you can replace it with class like that:
page.should have_selector("h1[class='title']", text: "#{base_title} Help)

Resources