I'm creating a user object using factory bot in an rspec feature spec.
The user belongs_to a company and has the below factory setup
FactoryBot.define do
factory :user do
company
auth_token { Faker::Internet.password }
email { Faker::Internet.email }
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
contact_phone { '+353871234567' }
status { true }
password { Faker::Internet.password }
password_confirmation { password }
end
end
When I create a user via rails console the factory works as expected
user = FactoryBot.create(:user)
The company is created and I can see the company_id as a UUID on the object
#<User id: "bb9fd4c7-bdce-4338-a42d-723876f514bc", company_id: "41e35b15-d766-4b1a-b833-bf1df4241064", first_name: "Frank", last_name: "Grimes", email: "drewshields#gislason.co"...
But when I use the same factory inside rspec feature spec both id and company_id fields are saved as integers not UUIDs
#<User id: 288, company_id: 0, first_name: "Bob", last_name: "Deckow", email: "raleighharber#leannon.info"...
The user creation in rspec is below
let(:user) { create(:user) }
Is there any reason why rspec would have this effect?
Full feature spec below:
# frozen_string_literal: true
# Specs for password reset
require 'rails_helper'
describe 'password reset', type: :feature do
let(:user) { create(:user) }
let(:invaliduser) {
invalid = build(:user, first_name: nil, contact_phone: '123')
invalid.save(validate: false)
invalid
}
context 'active user resets password' do
it 'sends an email to the user' do
visit root_path
first(:link, 'Login').click
click_on 'Forgotten Password?'
fill_in 'Email', with: user.email
click_on 'Reset Password'
expect(page).to have_content('If a matching email was found an email has been sent with password reset instructions.')
expect(ActionMailer::Base.deliveries.last.to).to include(user.email)
expect(ActionMailer::Base.deliveries.last.subject).to include('Please reset your password')
end
end
context 'inactive user resets password' do
it 'sends an email to the {{__}} team' do
visit root_path
first(:link, 'Login').click
click_on 'Forgotten Password?'
fill_in 'Email', with: invaliduser.email
click_on 'Reset Password'
expect(page).to have_content('If a matching email was found an email has been sent with password reset instructions.')
expect(ActionMailer::Base.deliveries.last.to).to include('{{team_email}}')
expect(ActionMailer::Base.deliveries.last.subject).to include('User Account Recovery')
end
end
end
Check that you have the same schema for development and test databases.
Easiest way to just drop and recreate testing DB
RAILS_ENV=test rake db:drop
RAILS_ENV=test rake db:setup
Ensure that you have UUID type ids in schema.rb
Related
i'm new at rails and i'm testing my devise gem User with capybara rspec and factory girl.
Here's spec code:
require 'rails_helper'
RSpec.describe User, type: :request do
it "displays the user's email after successful login" do
user = FactoryGirl.create(:user, email: 'test#test.com', password: 'password')
visit root_url
fill_in 'Email', with: 'test#test.com'
fill_in 'Password', with: 'password'
click_button 'Log in'
expect page.has_content?('jdoe')
end
end
The problem is in
expect page.has_content?('jdoe')
No matter what i put instead 'jdoe' test works perfectly without any errors.
Here's factory:
FactoryGirl.define do
factory :user do
email 'test#test.com'
password 'password'
password_confirmation 'password'
end
end
Maybe I missed something or going with a wrong way. How should I test here?
# spec/features/sessions_spec.rb
require 'rails_helper'
RSpec.feature "Sessions" do
scenario "displays the user's email after successful login" do
user = FactoryGirl.create(:user)
visit root_url
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
click_button 'Log in'
expect(page).to have_content("Signed in successfully")
# will give a false postitive if form is rerended?
expect(page).to have_content(user.email)
end
end
A few things here:
Use a feature and not a request spec for end to end testing.
Don't hardcode object attributes. The whole point of factories is that the factory takes care of generating unique data so that the tests don't give false positives due to residual state.
Use expect(page).to instead of expect page.should. expect(page).to sets a subject and uses a RSpec matcher which will show you the text of the page in an error message if it does not match.
For reasons outside of my control, I can't use RSpec for testing in my current project. I'm trying to test Devise Reset Password, and I can't seem to come up with something that works.
Here's what I have so far:
require 'test_helper'
class ResetPasswordTest < ActionDispatch::IntegrationTest
setup do
#user = users(:example)
end
test "reset user's password" do
old_password = #user.encrypted_password
puts #user.inspect
# This assertion works
assert_difference('ActionMailer::Base.deliveries.count', 1) do
post user_password_path, user: {email: #user.email}
end
# puts #user.reset_password_token => nil
# Not sure why this doesn't assign a reset password token to #user
patch "/users/password", user: {
reset_password_token: #user.reset_password_token,
password: "new-password",
password_confirmation: "new-password",
}
# I get a success here, but I'm not sure why, since reset password token is nil.
assert_response :success
# This assertion doesn't work.
assert_not_equal(#user.encrypted_password, old_password)
end
end
I've added some comments above to where things don't seem to be working. Does anyone have an insight, or an idea about how better to test this?
Devise tests the PasswordsController internally. I wanted to test my PasswordsController with extra functionality and went to Devise's controller tests for help in building tests for Devise's default functionality, then adding assertions for my new functionality into the test case.
As noted in other answers, you must obtain the reset_password_token. Instead of parsing a delivered email as in another solution here, you could obtain the token in the same way as Devise:
setup do
request.env["devise.mapping"] = Devise.mappings[:user]
#user = users(:user_that_is_defined_in_fixture)
#reset_password_token = #user.send_reset_password_instructions
end
Check out Devise's solution for the PasswordControllerTest:
https://github.com/plataformatec/devise/blob/master/test/controllers/passwords_controller_test.rb.
I figured out that you need to reload the user upon sending the password reset email and when putting the /user/password route. You also need to get the password token from the email as it is different from the one stored in the database.
require 'test_helper'
class ResetPasswordTest < ActionDispatch::IntegrationTest
setup do
#user = users(:example)
end
test "reset user's password" do
# store old encrypted password
old_password = #user.encrypted_password
# check to ensure mailer sends reset password email
assert_difference('ActionMailer::Base.deliveries.count', 1) do
post user_password_path, user: {email: #user.email}
assert_redirected_to new_user_session_path
end
# Get the email, and get the reset password token from it
message = ActionMailer::Base.deliveries[0].to_s
rpt_index = message.index("reset_password_token")+"reset_password_token".length+1
reset_password_token = message[rpt_index...message.index("\"", rpt_index)]
# reload the user and ensure user.reset_password_token is present
# NOTE: user.reset_password_token and the token pulled from the email
# are DIFFERENT
#user.reload
assert_not_nil #user.reset_password_token
# Ensure that a bad token won't reset the password
put "/users/password", user: {
reset_password_token: "bad reset token",
password: "new-password",
password_confirmation: "new-password",
}
assert_match "error", response.body
assert_equal #user.encrypted_password, old_password
# Valid password update
put "/users/password", user: {
reset_password_token: reset_password_token,
password: "new-password",
password_confirmation: "new-password",
}
# After password update, signed in and redirected to root path
assert_redirected_to root_path
# Reload user and ensure that the password is updated.
#user.reload
assert_not_equal(#user.encrypted_password, old_password)
end
end
require 'rails_helper'
feature 'User' do
let(:user) { create(:user) }
let(:new_password) { 'Passw0rd!' }
it 'reset password' do
visit '/'
click_link 'Forgot password?'
fill_in 'E-mail', with: user.email
expect do
click_button 'Send me reset password instructions'
end.to change(ActionMailer::Base.deliveries, :count).by(1)
expect(unread_emails_for(user.email)).to be_present
open_email(user.email, with_subject: 'Reset password instructions')
click_first_link_in_email
fill_in 'New password', with: new_password
fill_in 'Confirm new password', with: new_password
click_button 'Change password'
expect(page).to have_notice 'Your password has changed'
click_link 'Logout'
fill_in 'E-mail', with: user.email
fill_in 'Password', with: new_password
click_button 'Sign in'
expect(page).to have_notice 'Wellcome!'
end
end
This code require email_spec and FactoryGirl gems.
I have this spec that I want to translate to MiniTest.
describe User do
subject { build(:user, provider: 'foo') }
# don't validate presence of password when provider is present
it do
should_not validate_presence_of(:password)
end
end
I tried this. I am getting an error of undefined method 'should_not' for UserTest
class UserTest < ActiveSupport::TestCase
def setup
#user = build_stubbed(:user)
end
test "responds to name" do
assert_respond_to #user, :name
end
should validate_presence_of(:password)
test "do not validate presence of password when provider is present" do
build_stubbed(:user, provider: 'foo')
should_not validate_presence_of(:password)
end
end
I want to change the context for one test, where the subject gets a provider attribute, which should disable the presence validator on the password field.
Here's the full error:
UserTest#test_presence_of_password:
NoMethodError: undefined method `should_not' for #<UserTest:0x007feaa82c1c68>
test/models/user_test.rb:45:in `block in <class:UserTest>'
I found that the better way to do this is to revert to good old MiniTest:
test "uniqueness of email with a different provider" do
email_user = create(:user, email: "foo#bar.com")
facebook_user = build_stubbed(:facebook_user, email: "foo#bar.com")
assert facebook_user.valid?, "should be valid with same email if provider is different"
end
Take a look at the minitest-rails-shoulda gem. If you use it I assume the test would look like this:
describe User do
subject { build_stubbed(:user) }
it { must validate_presence_of(:password) }
describe "when a provider is present" do
subject { build_stubbed(:user, provider: 'foo') }
it { wont validate_presence_of(:password) }
end
end
Here is my Spec file:
require 'spec_helper'
describe User, "references" do
it { should have_and_belong_to_many(:roles) }
it { should belong_to(:account_type) }
it { should belong_to(:primary_sport).class_name("Sport") }
it { should belong_to(:school) }
it { should belong_to(:city) }
end
describe User, "factory" do
before(:each) do
#user = FactoryGirl.create(:user)
end
it "is invalid with no email" do
#user.email = nil
#user.should_not be_valid
end
it "is valid with email" do
#user.should be_valid
end
end
Factory:
FactoryGirl.define do
factory :user do
email Faker::Internet.email
password "password"
password_confirmation "password"
agreed_to_age_requirements true
end
end
The part I am trying to "test" for and not sure how to 100% is checking to make sure when a User is created that the email address is not nil.
shoulda provides validation helpers to help you test the validations.
it { should validate_presence_of(:email) }
If you want to use rspec and write your own, then
describe User do
it "should be invalid without email" do
user = FactoryGirl.build(:user, :email => nil)
#user.should_not be_valid
#user.errors.on(:email).should == 'can't be blank' #not sure about the exact message. But you will know when you run the test
end
it "should be valid with email" do
user = FactoryGirl.build(:user, :email => "user#user.com")
#user.should be_valid
end
end
When you run the test, it would read as
User
should be invalid without email
should be valid with email
Giving a good description for your test case is very important, because it kind of acts like a documentation.
In my rspec testing. I got NoMethodError:
undefined method `password_digest='.
But in rails console. I do can use .password_digest method.
I am so confused. I searched google. Got sever threads about this. even in stackoverflow. But those don't help. I do have password_digest as a field in my database.
I can even set my password_digest
see:
1.9.2-p290 :005 > user.password_digest
=> "$2a$10$X8CSsstOqZKKA6qVHpW9.uH5Lzd7dxfGNCAxvIbePpcfBg8KFbD4y"
1.9.2-p290 :006 > user.password_digest = 1
=> 1
1.9.2-p290 :007 > user.password_digest
=> 1
And also, in my app. everything seems fine. That's weird.
Please help...
1.9.2-p290 :002 > User.first.password_digest
User Load (0.3ms) SELECT "users".* FROM "users" LIMIT 1
=> "$2a$10$NCfX2SgKeqGARJ68StxRJuCbbbK7g18n5FPxbHY5THwg4pAdHUvui"
1.9.2-p290 :003 >
[5]+ Stopped rails console
luke#Macbook-Pro~/Documents/workspace/RoR/rails_projects/sample_app2012$ bundle exec rspec spec/models/user_spec.rb
DEPRECATION WARNING: The InstanceMethods module inside ActiveSupport::Concern will be no longer included automatically. Please define instance methods directly in #<Class:0x000001029f6688> instead. (called from <top (required)> at /Users/luke/Documents/workspace/RoR/rails_projects/sample_app2012/spec/models/user_spec.rb:14)
DEPRECATION WARNING: The InstanceMethods module inside ActiveSupport::Concern will be no longer included automatically. Please define instance methods directly in #<Class:0x000001029f6688> instead. (called from <top (required)> at /Users/luke/Documents/workspace/RoR/rails_projects/sample_app2012/spec/models/user_spec.rb:14)
FFFFFFFFFFFFFFFFFFFFF
Failures:
1) User
Failure/Error: #user = User.new(name: "Example User", email: "user#example.com",
NoMethodError:
undefined method `password_digest=' for #<User:0x00000100beee60>
# ./spec/models/user_spec.rb:17:in `new'
# ./spec/models/user_spec.rb:17:in `block (2 levels) in <top (required)>'
..... There are 19 more failures, to save space, i didn't paste them here.
And this is my user_spec.rb code:
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
require 'spec_helper'
describe User do
before(:each) do
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
end
subject { #user }
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should respond_to(:password_digest)}
it { should respond_to(:password)}
it { should respond_to(:password_confirmation)}
it { should respond_to(:remember_token) }
it { should respond_to(:authenticate) }
it { should be_valid }
describe "when name is not present" do
before { #user.name = " " }
it { should_not be_valid }
end
describe "when email is not present" do
before { #user.email = " " }
it { should_not be_valid }
end
describe "when name is too long" do
before { #user.name = "a" * 51 }
it { should_not be_valid }
end
describe "when email format is valid" do
valid_addresses = %w[user#foo.com THE_USER#foo.bar.org first.last#foo.jp]
valid_addresses.each do |valid_address|
before { #user.email = valid_address}
it {should be_valid }
end
end
describe "when email address is already taken" do
before do
user_with_same_email = #user.dup
user_with_same_email.email = #user.email.upcase
user_with_same_email.save
end
it { should_not be_valid }
end
describe "when password is not present" do
before { #user.password = #user.password_confirmation = " " }
it {should_not be_valid}
end
describe "when password doesn't match confirmation" do
before { #user.password_confirmation = "mismatch" }
it { should_not be_valid }
end
describe "return value of authenticate method" do
before { #user.save }
let(:found_user) { User.find_by_email(#user.email) }
describe "with valid password" do
it { should == found_user.authenticate(#user.password) }
end
describe "with invalid password" do
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
it { should_not == user_for_invalid_password }
specify { user_for_invalid_password.should be_false }
end
describe "remember token" do
before { #user.save }
its(:remember_token) { should_not be_blank }
end
end
end
And this is my user.rb under modles:
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
class User < ActiveRecord::Base
has_secure_password
attr_accessible :name, :email, :password, :password_confirmation
validates :name, presence: true, :length => { maximum: 50 }
valid_email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, :presence => true,
:format => { with: valid_email_regex },
:uniqueness => { case_sensitive: false }
validates :password, length: { minimum: 6}
end
Was just stuck on this for a while as well.
Don't forget
rake db:test:prepare after new migrations!
class User has method password_diges but has no method password_digest=
this is not the same methods, so instance of User shall not be responsible on password_digest
it shoud return password_digest but should_not respond_to password_digest i think
You can use User.first.password_digest in console because password_digest and password_digest= isn't the same methods.
password_digest= same as:
def password_digest=(digest)
#password_digest = digest
end
otherwise password_digest:
def password_digest
#password_digest
end
In your console, you're trying the password_digest method, not the password_digest= method. Are you able to execute the following in the console? User.first.password_digest = "$2a$10$NCfX2SgKeqGARJ68StxRJuCbbbK7g18n5FPxbHY5THwg4pAdHUvui"
I think your issue may be rooted in the fact that you're specifying only certain attributes in your User model as being attr_accessible. Try adding :password_digest to that list and see if your test passes.
Hi I am new to RoR and I learned RailsTutorial and encountered this issue too. After a long search in Google I found my db/test.sqlite3 hasn't been updated. which means there was 'password_digest' in my db/development.sqlite3 but there wasn't that column in my db/test.sqlite3.
And after I run the command 'rake db:reset' and 'rake db:test:prepare', the issue was gone.
I had the exactly same issue yesterday. The console in development and in test environment work well. And the web app itself works great as well. But the test (almost the same test as the question described) fail for no method error.
I've tried to stop spring, recreate test database, kill memcached. But it doesn't work.
Finally, I added some data into the fixture file test/fixtures/users.yml.
harry:
name: Harry
email: harry#example.com
password_digest: <%= User.digest('password') %>
It was empty before. Then the tests passed. I made the user.yml empty afterwards.The tests still passed.
I don't know why yet. But hope it helps.