Let's say I have this code:
require 'spec_helper'
describe "authorization" do
describe "for non-signed-in users" do
let(:user) { FactoryGirl.create(:user) }
describe "in the Users controller" do
describe "submitting to the update action" do
before { put user_path(user) }
specify { response.should redirect_to(signin_path) }
end
end
end
end
Instead of:
specify { response.should redirect_to(signin_path) }
Why couldn't I use:
its { response.should redirect_to(signin_path) }
AS Zippie, Juliano, apneadiving have stated:
specify its an alias to it
context its an alias to describe
You can use form its(:method) { expectation } to call methods on subject:
let(:bar) { Bar.new }
subject { bar }
its(:foo) { should == 0 }
to test this
class Bar
def foo
0
end
end
Related
is it possible to shorten this Rspec?
I'd like to extract the line it { expect { author.destroy }.to_not raise_error } not to repeat it in every context. Shared examples are some way, but finally, it generates more code than below redundant version.
require 'rails_helper'
RSpec.describe Author, type: :model do
describe 'destroying' do
context 'when no books assigned' do
subject!(:author) { FactoryBot.create :author_with_no_books }
it { expect { author.destroy }.to_not raise_error }
# other examples
end
context 'when there are some books' do
subject!(:author) { FactoryBot.create :author_with_books }
it { expect { author.destroy }.to_not raise_error }
# other examples
end
context 'when there are some posts' do
subject!(:author) { FactoryBot.create :author_with_posts }
it { expect { author.destroy }.to_not raise_error }
# other examples
end
end
end
Use shared_examples with a parameter instead of abusing subject:
RSpec.describe Author, type: :model do
include FactoryBot::Syntax::Methods # you can move this to rails_helper.rb
RSpec.shared_examples "can be destroyed" do |thing|
it "can be destroyed" do
expect { thing.destroy }.to_not raise_error
end
end
describe 'destroying' do
context 'without books' do
include_examples "can be destroyed", create(:author_with_no_books)
end
context 'with books' do
include_examples "can be destroyed", create(:author_with_books)
end
context 'with posts' do
include_examples "can be destroyed", create(:author_with_posts)
end
end
end
I have the following test. There are three it blocks. The first one doesn't use shoulda unlike the other two.
If I don't use the before block with post :create, product: attrs then the first test fails as expected. But If I put the before block there then the first test fails, but the other two pass. I have a uniqueness validation on product name, but that shouldn't be the problem as I'm using sequence with factory.
What should I do? How should I generally setup the data for testing when there are rspec and shoulda matchers present at the same time?
describe "when user logged in" do
before(:each) do
login_user #logged in user is available by calling #user
end
context "POST create" do
context "with valid attributes" do
let!(:profile) { create(:profile, user: #user) }
let!(:industry) { create(:industry) }
let!(:attrs) { attributes_for(:product, user_id: #user.id, industry_ids: [ industry.id ]).merge(
product_features_attributes: [attributes_for(:product_feature)],
product_competitions_attributes: [attributes_for(:product_competition)],
product_usecases_attributes: [attributes_for(:product_usecase)]
) }
it "saves the new product in the db" do
expect{ post :create, product: attrs }.to change{ Product.count }.by(1)
end
#If I don't use this the 2 tests below fail. If I use it, then the test above fails.
# before do
# post :create, product: attrs
# end
it { is_expected.to redirect_to product_path(Product.last) }
it { is_expected.to set_flash.to('Product got created!') }
end
end
end
factories
factory :product, class: Product do
#name { Faker::Commerce.product_name }
sequence(:name) { |n| "ABC_#{n}" }
company { Faker::Company.name }
website { 'https://example.com' }
oneliner { Faker::Lorem.sentence }
description { Faker::Lorem.paragraph }
user
end
You can't have it both ways. If you execute the method you are testing in the before, then you can't execute it again to see if it changes the Product count. If you don't execute it in your before, then you must execute it in your example and therefore can't use the is_expected one liner format.
There are a variety of alternatives. Here is one that incorporates the execution of the method into all the examples.
describe "when user logged in" do
before(:each) do
login_user #logged in user is available by calling #user
end
describe "POST create" do
subject(:create) { post :create, product: attrs }
context "with valid attributes" do
let!(:profile) { create(:profile, user: #user) }
let!(:industry) { create(:industry) }
let!(:attrs) { attributes_for(:product, user_id: #user.id, industry_ids: [ industry.id ]).merge(
product_features_attributes: [attributes_for(:product_feature)],
product_competitions_attributes: [attributes_for(:product_competition)],
product_usecases_attributes: [attributes_for(:product_usecase)]
) }
it "saves the new product in the db" do
expect{ create }.to change{ Product.count }.by(1)
end
it("redirects") { expect(create).to redirect_to product_path(Product.last) }
it("flashes") { expect(create).to set_flash.to('Product got created!') }
end
end
end
I am having a problem with the Rails Tutorial (rails 3). I'm currently on 9.28 rSpec tests and I have been in constant search to figure out what I did to make these two errors.
user#ubuntu:~/rails_projects/sample_app$ bundle exec rspec spec/
......................F.F............................................
Failures:
1) Authentication authorization in the Users controller visiting the edit page
Failure/Error: before { visit edit_user_path(user) }
NameError:
undefined local variable or method `user' for #<RSpec::Core::ExampleGroup::Nested_3::Nested_3::Nested_2::Nested_1:0x9685354>
# ./spec/requests/authentication_pages_spec.rb:75:in `block (5 levels) in <top (required)>'
2) Authentication authorization in the Users controller submitting to the update action
Failure/Error: before { put user_path(user) }
NameError:
undefined local variable or method `user' for #<RSpec::Core::ExampleGroup::Nested_3::Nested_3::Nested_2::Nested_3:0x9b12034>
# ./spec/requests/authentication_pages_spec.rb:85:in `block (5 levels) in <top (required)>'
Finished in 10.86 seconds
69 examples, 2 failures
Failed examples:
rspec ./spec/requests/authentication_pages_spec.rb:76 # Authentication authorization in the Users controller visiting the edit page
rspec ./spec/requests/authentication_pages_spec.rb:86 # Authentication authorization in the Users controller submitting to the update action
This is the authentication spec:
require 'spec_helper'
describe "Authentication" do
subject { page }
describe "signin page" do
before { visit signin_path }
it { should have_selector('h1', text: 'Sign in') }
it { should have_selector('title', text: full_title('Sign in')) }
end
describe "signin" do
before { visit signin_path }
describe "with invalid information" do
before{ click_button "Sign in" }
it { should have_selector('title', text: full_title('Sign in')) }
it { should have_selector('div.alert.alert-error', text: 'Invalid')}
describe "after visiting another page" do
before { click_link "Home" }
it { should_not have_selector ('div.alert.alert-error') }
end
end
describe "valid information" do
let (:user) { FactoryGirl.create(:user) }
before { sign_in user }
it { should have_selector('title', text: full_title(user.name)) }
it { should have_link('Users', href: users_path) }
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Settings', href: edit_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', href: signin_path) }
end
end
end
describe "authorization" do
describe "for non-signed-in users" do
let(:user) { FactoryGirl.create(:user) }
describe "when attempting to visit a protected page" do
before do
visit edit_user_path(user)
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
describe "after signing in" do
it "should render the desired protected page" do
expect(page).to have_selector('title', text: ('Edit user'))
end
end
end
end
describe "in the Users controller" do
describe "visiting the edit page" do
before { visit edit_user_path(user) }
it { should have_selector('title', text: full_title('Sign in')) }
end
describe "visiting the user index" do
before { visit users_path }
it { should have_selector('title', text: full_title('Sign in')) }
end
describe "submitting to the update action" do
before { put user_path(user) }
specify { response.should redirect_to(signin_path) }
end
end
describe "as wrong user" do
let(:user) { FactoryGirl.create(:user) }
let(:wrong_user) { FactoryGirl.create(:user, email: "wrong#example.com") }
before { sign_in user, no_capybara: true }
describe "submitting a GET request to the Users#edit action" do
before { get edit_user_path(wrong_user) }
specify { expect(response.body).not_to match(full_title('Edit user')) }
specify { expect(response).to redirect_to(root_url) }
end
describe "submitting a PUT request to the Users#update action" do
before { put user_path(wrong_user) }
specify { expect(response).to redirect_to(root_url) }
end
end
end
end
Really I have been trying to figure this out for quite sometime, if it is possible to walk through explanation ( if it is not typo ) would be awesome. Starting to grasp Rails but not quite there.
If you reindent the messy spec code, you can see that you are missing the user definition in your describe "in the Users controller" block.
Seeing that you are using the user in all three describe blocks in the Authorization, I'd move it one level up, so you'll end up with:
describe "authorization" do
let(:user) { FactoryGirl.create(:user) }
describe "for non-signed-in users" do
...
end
describe "in the Users controller" do
...
end
describe "as wrong user" do
let(:wrong_user) { FactoryGirl.create(:user, email: "wrong#example.com") }
before { sign_in user, no_capybara: true }
...
end
end
I'm following the RoR Tutorial and I'm stuck at Listing 9.15
I getting the following error after running 'bundle exec rspec spec/' :
1) Authentication authorization as wrong user submitting a PATCH request to the Users#update action
Failure/Error: specify { expect(response).to redirect_to(root_url) }
ArgumentError:
Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true
# ./spec/features/authentication_pages_spec.rb:79:in `block (5 levels) in <top (required)>'
My Authentication test code is:
require 'spec_helper'
describe "Authentication", type: :request do
subject { page }
describe "signin page" do
before { visit signin_path }
it { should have_content('Sign in') }
it { should have_title('Sign in') }
end
describe "signin" do
before { visit signin_path }
describe "with invalid information" do
before { click_button "Sign in" }
it { should have_title('Sign in') }
it { should have_selector('div.alert.alert-error', text: 'Invalid') }
describe "after visiting another page" do
before { click_link "Home" }
it { should_not have_selector('div.alert.alert-error') }
end
end
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before { sign_in user }
#it { should have_title(user.name) }
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Settings', href: edit_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
end
describe "authorization" do
describe "for non-signed-in users" do
let(:user) { FactoryGirl.create(:user) }
describe "in the Users controller" do
describe "visiting the edit page" do
before { visit edit_user_path(user) }
it { should have_title('Sign in') }
end
describe "submitting to the update action" do
before { patch user_path(user) }
specify { expect(response).to redirect_to(signin_path) }
end
end
end
describe "as wrong user" do
let(:user) { FactoryGirl.create(:user) }
let(:wrong_user) { FactoryGirl.create(:user, email: "wrong#example.com") }
before { sign_in user, no_capybara: true }
describe "visiting Users#edit page" do
before { visit edit_user_path(wrong_user) }
#it { should_not have_title(full_title('Edit user')) }
end
describe "submitting a PATCH request to the Users#update action" do
before { patch user_path(wrong_user) }
specify { expect(response).to redirect_to(root_url) }
end
end
end
end
I don't know how to resolve this issue so the test passes. How do I resolve it? Could someone explain what's going wrong? (According to the tutorial the test should be passing).
The problem may be that you have not defined default_host for test environment. Define default_host inside config/environments/test.rb like this:
config.action_mailer.default_url_options = {:host => "localhost:3000"}
In the end I just used:
root_path
instead of:
root_url
For tests like the controller tests shown in the OP's example, another setting is needed to resolve this error. Mentioned by Kesha Antonov you can instead use the Routes' default_url_options. By adding the following setting on the last line of your config/environments/test.rb (after the end), your tests will use the host given to build URLs:
Rails.application.routes.default_url_options[:host] = 'lvh.me:3000'
As mentioned by the OP in another answer, you can instead switch to a Path Named Route when the error is from a URL named route.
If you want to set the default host for mailers previews, you will want the action_mailer.default_url_options mentioned in other answers.
config.action_mailer.default_url_options = { :host => "localhost:3000" }
See Generating URLs in the Action Mailer API docs for more information on the action_mailer setting.
You've to set the default_url in each environment(development, test, production).
You need make these changes.
config/environments/development.rb
config.action_mailer.default_url_options =
{ :host => 'your-host-name' } #if it is local then 'localhost:3000'
config/environments/test.rb
config.action_mailer.default_url_options =
{ :host => 'your-host-name' } #if it is local then 'localhost:3000'
config/environments/development.rb
config.action_mailer.default_url_options =
{ :host => 'your-host-name' } #if it is local then 'localhost:3000'
You can avoid this error by using the skip_confirmation! method.
user = User.new(email: "example#example.com", password: "1234pass5678word")
user.skip_confirmation!
user.save
user
I am getting this error in relation to: "Authentication authorization in the Microposts controller submitting to the destroy action"
spec/requests.authentication_pages_spec.rb:119
The only solutions I have seen to this involve restarting the rails server and/or spork (which I am not using) have restarted the server though.
git://github.com/princeofburma/sample_app.git
spec/requests.authentication_pages_spec.rb
require 'spec_helper'
describe "Authentication" do
subject { page }
describe "signin page" do
before { visit signin_path }
it { should have_selector('h1', text: 'Sign in') }
it { should have_selector('title', text: 'Sign in') }
end
describe "signin" do
before { visit signin_path }
describe "with invalid information" do
before { click_button "Sign in" }
it { should have_selector('title', text: 'Sign in') }
it { should have_error_message }
describe "after visiting another page" do
before { click_link "Home" }
it { should_not have_error_message }
end
end
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before { sign_in 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 have_link('Settings', href: edit_user_path(user)) }
it { should have_link('Users', href: users_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
end
describe "authorization" do
describe "for non-signed-in users" do
let(:user) { FactoryGirl.create(:user) }
describe "when attempting to visit a protected page" do
before do
visit edit_user_path(user)
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
describe "after signing in" do
it "should render the desired protected page" do
page.should have_selector('title', text: 'Edit user')
end
describe "when signing in again" do
before do
click_link "Sign out"
click_link "Sign in"
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
it "should render the default (profile) page" do
page.should have_selector('title', text: user.name)
end
end
end
end
describe "in the Users controller" do
describe "visiting the edit page" do
before { visit edit_user_path(user) }
it { should have_selector('title', text: 'Sign in') }
it { should have_selector('div.alert.alert-notice') }
end
describe "submitting to the update action" do
before { put user_path(user) }
specify { response.should redirect_to(signin_path) }
end
describe "visiting the user index" do
before { visit users_path }
it { should have_selector('title', text: 'Sign in') }
end
describe "visiting the following page" do
before { visit following_user_path(user) }
it { should have_selector('title', text: 'Sign in') }
end
describe "visiting the followers page" do
before { visit followers_user_path(user) }
it { should have_selector('title', text: 'Sign in') }
end
end
end
describe "in the Microposts controller" do
describe "submitting to the create action" do
before { post microposts_path }
specify { response.should redirect_to(signin_path)}
end
describe "submitting to the destroy action" do
before { delete micropost_path(FactoryGirl.create(:micropost)) }
specify { response.should redirect_to(signin_path) }
end
end
describe "as wrong user" do
let(:user) { FactoryGirl.create(:user) }
let(:wrong_user) { FactoryGirl.create(:user, email: "wrong#example.com") }
before { sign_in user }
describe "visiting Users#edit page" do
before { visit edit_user_path(wrong_user) }
it { should_not have_selector('title', text: 'Edit user') }
end
describe "submitting a PUT request to the Users#update action" do
before { put user_path(wrong_user) }
specify { response.should redirect_to(root_path) }
end
end
describe "as non-admin user" do
let(:user) { FactoryGirl.create(:user) }
let(:non_admin) { FactoryGirl.create(:user) }
before { sign_in non_admin }
describe "submitting a DELETE request to the Users#destroy action" do
before { delete user_path(user) }
specify { response.should redirect_to(root_path) }
end
end
end
end
Here is my factories.rb file
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "Person #{n}" }
sequence(:email) { |n| "person_#{n}#example.com"}
password "foobar"
password_confirmation "foobar"
factory :admin do
admin true
end
end
factory :micropost do
content "Lorem ipsum"
user
end
end
here is the spec_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
# ## Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = "random"
end
ran the FactoryGirl.create(:micropost) from the test console and got:
irb(main):002:0> FactoryGirl.create(:micropost)
ArgumentError: Factory not registered: micropost
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/factory_girl-4
.1.0/lib/factory_girl/registry.rb:24:in `find'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/factory_girl-4
.1.0/lib/factory_girl/decorator.rb:10:in `method_missing'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/factory_girl-4
.1.0/lib/factory_girl.rb:71:in `factory_by_name'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/factory_girl-4
.1.0/lib/factory_girl/factory_runner.rb:12:in `run'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/factory_girl-4
.1.0/lib/factory_girl/strategy_syntax_method_registrar.rb:19:in `block in define
_singular_strategy_method'
from (irb):2
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/railties-3.2.9
/lib/rails/commands/console.rb:47:in `start'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/railties-3.2.9
/lib/rails/commands/console.rb:8:in `start'
from C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/railties-3.2.9
from script/rails:6:in `require
from script/rails:6:in `<main>'
i think your problem is in your spec_helper.rb
take a look how it manages factories definitions
https://gist.github.com/scottvrosenthal/1724370
i hope it helps.
I downloaded your project and I am not getting that error. The only problem that I had seen in your application is that you don't have a remember_token method in your User model.
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :remember_token
...
private
def create_remember_token
# NoMethodError: undefined method `remember_token=' for User
self.remember_token = SecureRandom.urlsafe_base64
end
end