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
Related
I am creating factories to replace my fixtures and to generate Test Data. I've installed FactoryBot but I am getting an error when running "rspec". This is the outcome log I get:
An error occurred while loading ./spec/controllers/users_controller_spec.rb.
Failure/Error: #user = FactoryBot.create(:user)
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
# ./spec/controllers/users_controller_spec.rb:5:in `block in <top (required)>'
# ./spec/controllers/users_controller_spec.rb:3:in `<top (required)>'
Finished in 0.00041 seconds (files took 38.38 seconds to load)
0 examples, 0 failures, 1 error occurred outside of examples
This is my spec/factories/user_factory.rb
FactoryBot.define do
factory :user do
email "peter#example.com"
password "0123456"
first_name "Peter"
last_name "Example"
admin false
end
end
This is my spec/controllers/users_controller_spec.rb
require 'rails_helper'
describe UsersController, type: :controller do
#user = FactoryBot.create(:user)
# let (:user) { User.create!(email:"achochaocierva#gmail.com", password: "Ratadecierva2")}
describe 'GET #show' do
context 'when a user is logged in' do
before do
sign_in user
end
it 'loads correct user details' do
get :show, params: { id: user.id }
expect(response).to be_ok
expect(assigns(:user)).to eq user
end
end
context 'when a user is not logged in' do
it 'redirects to login' do
get :show, params: { id: user.id }
expect(response).to redirect_to(new_user_session_path)
end
end
end
end
What would be the errors meaning? What am I doing wrong?
What would be the errors meaning? What am I doing wrong?
Your factory tries to create a user with the same email as one of the existing users (no wonder, seeing that the email is hardcoded in the factory). Your DB's uniqueness constraint complains.
You should use sequences for user emails
# email "peter#example.com"
sequence(:email) { |n| "factory_#{n}#example.com" }
First user from this factory will have email "factory_1#example.com", second - "factory_2#example.com", and so on.
I am a newbie to ruby and am just creating my first test suite.
When writing a minitest to destroy a user I get the following error:
ERROR["test_should_destroy_when_logged_in_as_a_admin", UsersControllerTest]
test_should_destroy_when_logged_in_as_a_admin#UsersControllerTest ActionController::UrlGenerationError:
No route matches {:action=>"/users/608331937", :controller=>"users"}
The test reads the following:
def setup
#user_destroy = users(:destroyme)
#user_admin = users(:admin)
end
test "should destroy when logged in as a admin" do
log_in_as(#user_admin)
assert #user_admin.admin?, "not admin"
assert_difference 'User.count', -1 do
delete user_path(#user_destroy)
end
end
and fixture:
admin:
name: Matthias Havenaar
email: my#mail.com
password_digest: <%= User.digest('password') %>
admin: true
destroyme:
name: Destroy Me
email: destroy#me.com
password_digest: <%= User.digest('password') %>
admin: true
It seems like something goes wrong with the user ID or user_path. Any idea what I am doing wrong here?
Try this, I hope this will work.
Replace
delete user_path(#user_destroy)
With
delete :destroy, id: #user_destroy
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
I have a request spec which passes up until the point where I need to check to see if content is present on the page, which I am using page.should have_content to do. The content is actually a message which appears on successful form submission, which is rendered from a messages partial. The test fails even though If I test through the browser, functionality works as expected and the content appears as it should. I'm also using FactoryGirl to generate the users to use for the form submission.
Here's the error I get after running the spec with the --format d option:
UserSignup
shows a thank you message on successful form submission (FAILED - 1)
Failures:
1) UserSignup shows a thank you message on successful form submission
Failure/Error: page.should have_content("Thank you. You will be notified of our launch at #{user.email}.")
expected #has_content?("Thank you. You will be notified of our launch at quinn.purdy#casperzboncak.org.") to return true, got false
# ./spec/requests/user_signup_spec.rb:21:in `block (2 levels) in <top (required)>'
user_signup_spec.rb:
require 'spec_helper'
describe "UserSignup" do
it "shows a thank you message on successful form submission" do
user = FactoryGirl.create(:user)
visit sign_up_path
fill_in "user_fullname", with: user.fullname
fill_in "user_email", with: user.email
click_button "Sign up"
current_path.should eq(sign_up_path)
page.should have_content("Thank you. You will be notified of our launch at #{user.email}.")
end
end
users_controller.rb:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(secure_params)
if #user.valid?
#user.subscribe
flash[:notice] = "Thank you. You will be notified of our launch at #{#user.email}."
redirect_to sign_up_path
else
render :new
end
end
private
def secure_params
params.require(:user).permit(:fullname, :email)
end
end
I'm wondering if it could be because I render the messages partial from the application layout, but when it gets outputted in the users view, the message appears inside the body of the source, but outside the main class.
So I seem to have got the tests passing by adding the line , :js => true do beside 'it' and using the selenium web driver. There's got to be a way to do it without selenium I'm thinking, because you have to sit and wait while it actually runs it in a browser which is the downside.
Maybe I'm going about it the wrong way, and I should actually be checking for the partial in a view spec (currently it was just part of the feature test).
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)