am relatively new with rspec and testing,am trying to write a test for this code
#assessments = Assessment.where(assigned_user_id: current_user.id)
here is what i have tried so far,but am not sure if it's the right way,i was told controller spec is not advisable,hence i am trying to implement it using unit test.
spec_file
require 'rails_helper'
# Test suite for the assessment model
RSpec.describe Assessment, type: :model do
let(:admin) { User.new(email: 'rockcoolsaint#example.com', password: 'password', password_confirmation: 'password', role: :admin) }
let(:behavioral_specialist) { User.new(email: 'bs#example.com', password: 'password', password_confirmation: 'password', role: :behavioral_specialist) }
let(:behavioral_specialist_2) { User.new(email: 'bs2#example.com', password: 'password', password_confirmation: 'password', role: :behavioral_specialist) }
let(:bs_user_assessments) { Assessment.where(assigned_user_id: behavioral_specialist.id) }
describe 'Validations' do
it { should validate_presence_of(:title) }
it 'is valid with all attributes' do
expect(build(:assessment, user: admin, assigned_user: behavioral_specialist)).to be_valid
end
it 'is valid if assigned_user_id equals behavioral_specialist_id' do
bs_user_assessments.each do |assessment|
expect(assessment.assigned_user_id).to eq(behavioral_specialist.id)
end
end
it 'is valid if assigned_user_id does not equal behavioral_specialist_2_id' do
bs_user_assessments.each do |assessment|
expect(assessment.assigned_user_id).to_not eq(behavioral_specialist_2.id)
end
end
end
Related
I got two tests that are failing in my application. I see that the problem is with the user's ID but I don't know how to solve it.
1) User return value of authenticate method with valid password
Failure/Error: it { should eq found_user.authenticate(#user.password) }
expected: #<User id: 2, name: "Example User", email: "user#example.com", created_at: "2014-08-09 17:17:22", updated_at: "2014-08-09 17:17:22", password_digest: "$2a$04$kQk6PkLu3X6rbTPiDDPce.zy6VYtAZ2rxchGLSP27VaG...">
got: #<User id: nil, name: "Example User", email: "user#example.com", created_at: nil, updated_at: nil, password_digest: "$2a$04$dccvbYxyrCa2M1LjxmwQqOBEEhYLNKrFmUh0GlAxH1bH...">
(compared using ==)
Diff:
## -1,2 +1,2 ##
-#<User id: 2, name: "Example User", email: "user#example.com", created_at: "2014-08-09 17:17:22", updated_at: "2014-08-09 17:17:22", password_digest: "$2a$04$kQk6PkLu3X6rbTPiDDPce.zy6VYtAZ2rxchGLSP27VaG...">
+#<User id: nil, name: "Example User", email: "user#example.com", created_at: nil, updated_at: nil, password_digest: "$2a$04$dccvbYxyrCa2M1LjxmwQqOBEEhYLNKrFmUh0GlAxH1bH...">
# ./spec/models/user_spec.rb:102:in `block (4 levels) in <top (required)>'
2) User email address with mixed case should be saved as all lower-case
Failure/Error: expect(#user.reload.email).to eq mixed_case_email.downcase
ActiveRecord::RecordNotFound:
Couldn't find User without an ID
# ./spec/models/user_spec.rb:72:in `block (3 levels) in <top (required)>'
Here is my user_spec.rb file :
describe User do
before 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(:authenticate)}
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 invalid" do
it "should be invalid" do
addresses = %w[user#foo,com user_at_foo.org example.user#foo.
foo#bar_baz.com foo#bar+baz.com]
addresses.each do |invalid_address|
#user.email = invalid_address
expect(#user).not_to be_valid
end
end
end
describe "when email format is valid" do
it "should be valid" do
addresses = %w[user#foo.COM A_US-ER#f.b.org frst.lst#foo.jp a+b#baz.cn]
addresses.each do |valid_address|
#user.email = valid_address
expect(#user).to be_valid
end
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 "email address with mixed case" do
let(:mixed_case_email) { "Foo#ExAMPle.CoM" }
it "should be saved as all lower-case" do
#user.email = mixed_case_email
#user.save
expect(#user.reload.email).to eq mixed_case_email.downcase
end
end
#PASSWORD
describe "when password is not present" do
before do
#user = User.new(name: "Example User", email: "user#example.com",
password: " ", password_confirmation: " ")
it { should_not be_valid }
end
end
describe "when password doesn't match confirmation" do
before { #user.password_confirmation = "mismatch" }
it { should_not be_valid }
end
describe "with a password that's too short" do
before { #user.password = #user.password_confirmation = "a" * 5 }
it { should be_invalid }
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 eq found_user.authenticate(#user.password) }
end
describe "with invalid password" do
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
it { should_not eq user_for_invalid_password }
specify { expect(user_for_invalid_password).to be_false }
end
end
end
And the user.rb file
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
end
Change
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
to
#user = User.create(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
new just creates a model but doesnt save it to DB whereas
create creates the model AND saves it to DB
other option is to use save
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
#user.save
(For the 2ยบ test) You have to save the user in a before block
describe "email address with mixed case" do
let(:mixed_case_email) { "Foo#ExAMPle.CoM" }
before do
#user.email = mixed_case_email
#user.save
end
it "should be saved as all lower-case" do
expect(#user.reload.email).to eq mixed_case_email.downcase
end
end
That is the problem which causes "Couldn't find User without an ID"
The error is here:
specify { expect(user_for_invalid_password).to be_false }
this line is wrong, correct one is:
specify { expect(user_for_invalid_password).to be false }
I'm following the Rails Tutorial Modeling Users Chapter: http://www.railstutorial.org/book/modeling_users#cha-modeling_users.
My user.rb looks like:
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
has_secure_password
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[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
and my user model spec looks like:
describe User, :type => :model do
before 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(:authenticate) }
it { should be_valid }
... (other methods are here)
describe "when password is not present" do
before do
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
end
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 eq found_user.authenticate(#user.password) }
end
describe "with invalid password" do
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
it { should_not eq user_for_invalid_password }
specify { expect(user_for_invalid_password).to be_false }
end
end
Which I'm pretty sure is exactly a duplication what the Rails Tutorial code is, but I'm getting the following failed test errors:
rspec ./spec/models/user_spec.rb:83 # User when password is not present should not be valid
rspec ./spec/models/user_spec.rb:108 # User return value of authenticate method with invalid password should be false
I checked this out by looking at the Ruby on Rails Tutorial Book's source code (Rails 4) at GitHub: spec/models/user_spec.rb. Based on the code there, it looks like your passwords are currently of acceptable type and that's why your test is failing. I mean your passwords are good. foobar is a valid password. Below an empty string is passed to the User model validation.
describe "when password is not present" do
before do
#user = User.new(name: "Example User", email: "user#example.com",
password: " ", password_confirmation: " ")
end
it { should_not be_valid }
end
The second one I'm not sure, but would it help if you tried the Rails 3 spec/models/user_spec.rbrelated code for the same test:
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
It looks slightly different but it's testing the same thing. This is just suggestion, because I'm not sure what's going wrong.
It looks like a couple of methods are missing (if you're trying to match the tutorial exactly) Listing 6.25 & 6.28:
describe "when password doesn't match confirmation" do
before { #user.password_confirmation = "mismatch" }
it { should_not be_valid }
end
describe "with a password that's too short" do
before { #user.password = #user.password_confirmation = "a" * 5 }
it { should be_invalid }
end
I'm told this is the correct way to write a controller it block:
describe UsersController do
let(:user){ mock_model(User, id: 2, name: "Jimbo", email: 'jimbo#email.com', password: 'passwordhuzzah', password_confirmation: 'passwordhuzzah') }
describe 'PATCH #update' do
it "should fail in this case" do
User.should_receive(:find).with(user.id.to_s).and_return user
user.should_receive(:update_attributes).with({ "email" => user.email, "name" => user.name, "password" => user.password, "password_confirmation" => user.password_confirmation }).and_return true
patch :update, id: user.id, user: { email: user.email, name: user.name, password: user.password, password_confirmation: user.password_confirmation }
flash[:error].should == "could not update user"
response.status.should == 200
end
end
end
But I am surprised, because it doesn't seem DRY to me. Let's say I want to create two 'contexts' here. One where the user.update_attributes call returns false and returns true. Am I meant to simply copy paste two it blocks and tweak that one tiny argument?
describe UsersController do
let(:user){ mock_model(User, id: 2, name: "Jimbo", email: 'jimbo#email.com', password: 'passwordhuzzah', password_confirmation: 'passwordhuzzah') }
it "should pass in this case" do
User.should_receive(:find).with(user.id.to_s).and_return user
user.should_receive(:update_attributes).with({ "email" => user.email, "name" => user.name, "password" => user.password, "password_confirmation" => user.password_confirmation }).and_return true
patch :update, id: user.id, user: { email: user.email, name: user.name, password: user.password, password_confirmation: user.password_confirmation }
flash[:error].should == "updated user"
response.status.should == 302
end
it "should fail in this case" do
User.should_receive(:find).with(user.id.to_s).and_return user
user.should_receive(:update_attributes).with({ "email" => user.email, "name" => user.name, "password" => user.password, "password_confirmation" => user.password_confirmation }).and_return false
patch :update, id: user.id, user: { email: user.email, name: user.name, password: user.password, password_confirmation: user.password_confirmation }
flash[:error].should == "could not update user"
response.status.should == 200
end
end
end
I was hoping rspec would allow me to write this sort of structure:
describe 'PATCH #update' do
before {}
context 'when attributes can be updated' do
before {}
it "should set the flash" do end
it "should set the status" do end
end
context 'when attributes can\'t be updated' do
before {}
it "should set the status" do end
it "should set the flash" do end
end
end
Note the multiple it blocks for the same contexts, because isn't one expectation per it block a good practise because it allows you to see exactly what's not working? How are you meant to do this with mocks?
Model Validation:
validates :username, uniqueness: true, format: { with: /\A[a-zA-Z0-9_.-#]+\Z/i, message: "must contain only letters, numbers or _*-#" }, on: :update, :if => :username_changed?
Rspec:
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) }
it { should validate_presence_of(:email) }
it { should validate_uniqueness_of(:email) }
it { should allow_value("test#test.com").for(:email) }
it { should_not allow_value("test.com").for(:email) }
describe "validation of username", focus: true do
before(:each) do
#user = User.new(email: Faker::Internet.email, password: "password", password_confirmation: "password", username: "test123", agreed_to_age_requirements: true)
end
it "should be valid" do
#user.save
#user.should be_valid
end
it "should not be valid with incorrect characters in username" do
#user.username = "test###!!!"
#user.should_not be_valid
end
end
end
FactoryGirl:
FactoryGirl.define do
factory :user do
email Faker::Internet.email
password "password"
password_confirmation "password"
agreed_to_age_requirements true
username Faker::Internet.user_name
end
end
I am basically just trying to test against the custom validation for the uniqueness of and with the specified format of the username
i have completed the rails tutorial http://ruby.railstutorial.org/, and trying to implement the user model, but in MongoDB, using MongoID.
I have the following code, which is almost exactly the same as the tutorial, yet it's failing on one of the test cases.
class User
include Mongoid::Document
include ActiveModel::SecurePassword
attr_accessible :name, :email, :password, :password_confirmation
field :name, type: String
field :email, type: String
field :password_digest, type: String
has_secure_password
before_save { |user| user.email = email.downcase }
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, presence: true, length: { maximum: 50}
validates :email, presence: true, format: { with: email_regex },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 5}
validates :password_confirmation, presence: true
end
and rspec tests:
require 'spec_helper'
describe User do
before 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(:authenticate) }
describe 'without a name' do
before { #user.name = "" }
it { should_not be_valid }
end
describe 'without an email' 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 invalid" do
it "should be invalid" do
addresses = %w[user#foo,com user_at_foo.org example.user#foo.
foo#bar_baz.com foo#bar+baz.com]
addresses.each do |invalid_address|
#user.email = invalid_address
#user.should_not be_valid
end
end
end
describe "when email format is valid" do
it "should be valid" do
addresses = %w[user#foo.COM A_US-ER#f.b.org frst.lst#foo.jp a+b#baz.cn]
addresses.each do |valid_address|
#user.email = valid_address
#user.should be_valid
end
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 "when password confirmation is nil" do
before { #user.password_confirmation = nil }
it { should_not be_valid }
end
describe "with a password that's too short" do
before { #user.password = #user.password_confirmation = "a" * 5 }
it { should be_invalid }
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
end
end
The failure on the spec is:
........F..........
Failures:
1) User return value of authenticate method with valid password
Failure/Error: it { should == found_user.authenticate(#user.password) }
expected: #<User _id: 50e30a678bffa61a9e000001, _type: nil, name: "Example User", email: "user#example.com", password_digest: "$2a$10$C6klW7B9ok4OIHFopy5AfuncvwNYTZqHWTbqpdd8gXmruSLlwizPO">
got: #<User _id: 50e30f158bffa6df67000001, _type: nil, name: "Example User", email: "user#example.com", password_digest: "$2a$10$mDCz9JVPXGSvpGBwbyHIsuV/k.lJ/ux1TuvgRzxSLYlIna/CdztN2"> (using ==)
Diff:
## -1,2 +1,2 ##
-#<User _id: 50e30a678bffa61a9e000001, _type: nil, name: "Example User", email: "user#example.com", password_digest: "$2a$10$C6klW7B9ok4OIHFopy5AfuncvwNYTZqHWTbqpdd8gXmruSLlwizPO">
+#<User _id: 50e30f158bffa6df67000001, _type: nil, name: "Example User", email: "user#example.com", password_digest: "$2a$10$mDCz9JVPXGSvpGBwbyHIsuV/k.lJ/ux1TuvgRzxSLYlIna/CdztN2">
# ./spec/models/user_spec.rb:89:in `block (4 levels) in <top (required)>'
Finished in 1.79 seconds
19 examples, 1 failure
Update:
Did a bit more diggig, and querying the test database it seems that the data is not being cleared before each test case.
Updat2:
I have managed to fix this by including the following in my Spec Helper:
config.before :each do
Mongoid.purge!
end
But tests are noticeably slower now! any idea how to speed it up?
The problem is calling save, which recreates the password_digest
describe "return value of authenticate method" do
#before { #user.save } #comment this line
#....
end