I've been working through the Ruby on Rails Tutorial. I've run into a problem getting a test to pass that checks for the a mass assignment security exception to be thrown. I'm not sure why I'm getting this test failure, or how to fix it.
rspec:
describe "accessible attributes" do
it "should not allow access to user_id" do
expect do
Micropost.new(user_id: user.id)
end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
end
Failures:
1) Micropost accessible attributes should not allow access to user_id
Failure/Error: expect { Micropost.new(user_id: user.id) }.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
expected ActiveModel::MassAssignmentSecurity::Error, got #<NoMethodError: undefined method `call' for #<RSpec::Expectations::ExpectationTarget:0x8af2bb8>>
# ./spec/models/micropost_spec.rb:23:in `block (3 levels) in <top (required)>
Try using to instead of should for your expect raise_error matcher.
describe "accessible attributes" do
it "should not allow access to user_id" do
expect do
Micropost.new(user_id: user.id)
end.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
end
Related
I'm writing a test for my Rails 5 API to confirm a password_reset_token is generated whenever a user requests a password reset.
I have a short password_reset_controller that handles the initial request:
class PasswordResetController < ApplicationController
def new
#user = User.find_by(email: params[:email].to_s.downcase)
#user.send_password_reset if #user
end
end
send_password_reset is a method in my user model which calls the the generate_password_reset_instructions:
def send_password_reset
self.generate_password_reset_instructions
UserMailer.password_reset(self).deliver
end
def generate_password_reset_instructions
self.password_reset_token = SecureRandom.hex(10)
self.password_reset_sent_at = Time.now.utc
save
end
This should save the password_reset_token to my db (indeed, when I debug it, I know it goes through these lines of code). The following test suite passes with the exception of the last one:
require 'rails_helper'
RSpec.describe 'Password Reset API' do
let!(:user) { create(:user) }
describe 'POST password_reset/new' do
it 'returns status code 204 with user found' do
post '/password_reset/new', params: {email: user.email}
expect(response).to have_http_status(204)
end
it 'returns status code 204 with no user found' do
post '/password_reset/new', params: {email: 'not#anemail.com'}
expect(response).to have_http_status(204)
end
it "sends an email" do
expect { UserMailer.password_reset(user).deliver }.to change { ActionMailer::Base.deliveries.count }.by(1)
end
it 'confirmation_email is sent to the right user' do
UserMailer.password_reset(user).deliver
mail = ActionMailer::Base.deliveries.last
expect(mail.to[0]).to eq user.email
end
it 'generates password_reset_token' do
expect { post '/password_reset/new', params: {email: user.email} }.to change { user.password_reset_token }
end
end
end
The result I get in the terminal is:
Failures:
1) Password Reset API POST password_reset/new generates password_reset_token
Failure/Error: expect { post '/password_reset/new', params: {email: user.email} }.to change { user.password_reset_token }
expected result to have changed, but is still nil
# ./spec/requests/password_reset_spec.rb:28:in `block (3 levels) in <top (required)>'
# ./spec/rails_helper.rb:56:in `block (3 levels) in <top (required)>'
# ./spec/rails_helper.rb:55:in `block (2 levels) in <top (required)>'
This seems like it might be caused by the value not saving to the test db, but following suggestions in other posts (Like moving ENV['RAILS_ENV'] ||= 'test') to the top of rails_helper.rb does not solve the problem. I know that the actual methods work, as I'm able to get the desired result manually by running the server and making requests, I just can't write a test to confirm that it is indeed working. As the syntax in my test would indicate, I'm using FactoryGirl in my tests to generate users.
Any ideas as to why the last test (it 'generates password_reset_token' do) does not pass?
RSpec.describe 'Password Reset API' do
let!(:user) { create(:user) }
....
it 'generates password_reset_token' do
expect { post '/password_reset/new', params: {email: user.email} }.to change { user.password_reset_token }
end
end
What you expect:
You're creating a User and expecting it to be changed.
What is happening:
You create a Ruby User object in your test
You expect that object to be changed
Your Rails code initializes a new User object which updates the database and changes the attribute
Your test object remains untouched
What you need to do is to reload your test object with fresh attribute values from the database using user.reload.password_reset_token
I try to create test for users-factories.But when I running test, 'rspec' show me an error
Failure/Error: expect(:user).to be_valid
NoMethodError:
undefined method `valid?' for :user:Symbol
this is my user_spec.rb
require 'rails_helper'
RSpec.describe do
it "has a valid factory" do
user = FactoryGirl.create(:user)
expect(:user).to be_valid
end
end
and this is users-factories(with 'faker' gem)
FactoryGirl.define do
factory :user do
email Faker::Internet.email
password Faker::Internet.password(8)
end
end
how fix and why this method don't work?
sorry for my bad English
Here is a fix :
RSpec.describe do
it "has a valid factory" do
user = FactoryGirl.create(:user)
expect(user).to be_valid
end
end
You passed the symbol :user as an argument to the expect(:user), which you shouldn't. You should pass the local variable user, you have created, and passed to it expect(user).
I have problem with testing my controller. I've test post request in 2 context. With valid attributes and with invalid attributes. I have problem with valid attributes context.
I have the following test:
describe "POST #create" do
context "with valid attributes" do
it "saves the new car_configuration in the database" do
expect{
post :create, car_configuration: attributes_for(:car_configuration)
}.to change(CarConfiguration, :count).by(1)
end
it "redirects to the index car_configuration" do
post :create, car_configuration: attributes_for(:car_configuration)
should redirect_to admin_car_configurations_url
end
end
end
And my car_configuration factory is:
FactoryGirl.define do
factory :car_configuration do
sequence(:image) {|n| "Image #{n}"}
small_cases_count 5
big_cases_count 2
association :body_style, factory: :car_body_style
association :model, factory: :car_model
association :car_class
end
end
And errors that rspec shows:
1) Admin::CarConfigurationsController POST #create with valid attributes saves the new car_configuration in the database
Failure/Error: expect{
count should have been changed by 1, but was changed by 0
# ./spec/controllers/admin/car_configurations_controller_spec.rb:42:in `block (4 levels) in <top (required)>'
2) Admin::CarConfigurationsController POST #create with valid attributes redirects to the index car_configuration
Failure/Error: should redirect_to admin_car_configurations_url
Expected response to be a <redirect>, but was <200>
# ./spec/controllers/admin/car_configurations_controller_spec.rb:49:in `block (4 levels) in <top (required)>'
spec/controllers/admin/car_configuratoins_controller_spec.rb
require 'spec_helper'
describe Admin::CarConfigurationsController do
let(:configuration) { build :configuration }
context "POST create" do
it "creates a config" do
expect { perform }.to change(CarConfiguration, :count).by(1)
end
def perform
post :create, car_configuration: configuration.attributes
end
end
end
I try to pass these two create controller specs but for some reason it it not validating the Item object. Could it be that FactoryGirl.attributes_for(:item) are missing the profile and attachment required associations? If so, how can I pass it to attributes too?
describe "POST #create" do
context "signed in" do
login_user
context "with valid attributes" do
it "creates a new item" do
expect{
post :create, trend: FactoryGirl.attributes_for(:item)
}.to change(Item,:count).by(1)
end
it "redirects to the home page" do
post :create, item: FactoryGirl.attributes_for(:item)
response.should redirect_to Item.last
end
end
end
I got these errors
Failures:
1) ItemsController POST #create signed in with valid attributes creates a new item
Failure/Error: expect{
count should have been changed by 1, but was changed by 0
# ./spec/controllers/items_controller_spec.rb:42:in `block (5 levels) in <top (required)>'
2) ItemsController POST #create signed in with valid attributes redirects to the home page
Failure/Error: response.should redirect_to Item.last
Expected response to be a <redirect>, but was <200>
# ./spec/controllers/items_controller_spec.rb:48:in `block (5 levels) in <top (required)>'
This is the Item factory
FactoryGirl.define do
factory :item do
profile
after(:build) do |item|
item.attachments << FactoryGirl.build(:attachment, attachable: item)
end
end
end
I guess doing FactoryGirl.attributes_for isn't actually a build, so the after(:build) won't fire. How about this:
FactoryGirl.attributes_for(:item, :profile_attributes => FactoryGirl.attributes_for(:profile), :attachments => [FactoryGirl.attributes_for(:subject)])
Probably a good idea to try this line in the rails console first to see what the hash comes out like :)
I'm wrestling with Harlt's Chapter 9 exercise 9.
When I design my test for this exercise with :no_capybara set to true, like in other sections of the tutorial, the test PASSES, but I get the following warning:
WARNING: ignoring the provided expectation message argument (true) since it is not a string.
This version of the test is here:
*spec/requests/authentication_pages_spec.rb*
describe "as an admin user" do
let(:admin) {FactoryGirl.create(:admin)}
before do
sign_in(admin, :no_capybara => true)
end
describe "should not be able to delete itself by direclty submitting request" do
before { delete user_path(admin) }
specify { response.should redirect_to(users_path),
flash[:error].should =~ /Can't delete own admin account/i }
end
end
Note this this is how Hartl uses that method in other spaces of the tutorial, as follows:
*spec/requests/authentication_pages_spec.rb*
describe 'signed in user' do
let(:user) { FactoryGirl.create(:user) }
before { sign_in user, no_capybara: true }
describe 'unable to access now' do
before {get new_user_path}
specify { expect(response).to redirect_to(root_url)}
end
.
.
.ect...
However, when I do not set :no_capybara, my test fails:
describe "as an admin user" do
let(:admin) {FactoryGirl.create(:admin)}
before { sign_in(admin) }
.
.
.
Failures:
1) Authentication authorization as a non-admin user submitting a DELETE request to the Users#destroy action
Failure/Error: before { delete user_path(user)}
NoMethodError:
undefined method `admin?' for nil:NilClass
# ./app/controllers/users_controller.rb:68:in `admin_user'
# ./spec/requests/authentication_pages_spec.rb:74:in `block (5 levels) in <top (required)>'
My two main questions are:
Why is that warning occurring during that test, but not in other tests where I pass :no_capybara to the sign_in method?
Why is my test failing if I don't pass it no_capybara? I've seen the other questions on stackoverflow related to this exercise, and other people's solutions don't require no_capybara.
Below is all code within my app that might be applicable. If I should include more, please let me know.
*users_controller.rb*
before_action :admin_user, only: :destroy
def destroy
user = User.find(params[:id])
if !current_user?(user)
user.destroy
flash[:success] = "User destroyed. ID: #{user.id}"
else
flash[:error] = "Can't delete own admin account"
end
redirect_to users_path
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
*controllers/helpers/session_helper.rb*
def current_user
remember_token = User.encrypt(cookies[:remember_token])
#current_user ||= User.find_by(remember_token: remember_token)
end
I think in second case rails couldn't able to find admin? method since its not present.
Thats why you are getting this error
NoMethodError:
undefined method `admin?' for nil:NilClass
did you ran these codes
$ rails generate migration add_admin_to_users admin:boolean
$ bundle exec rake db:migrate
$ bundle exec rake test:prepare
As per your error, i think rails is looking for a method instead of just checking boolean value for admin.