How to output values of rspec subject to debug? - ruby-on-rails

I can't figure out why my validation is failing. How can I output the value of my rspec subject i.e. model built from factory_girl
I am doing this test which is failing:
let(:factory_instance) { build(:user)}
it { should allow_value("abc123").for(:password) }
Test is failing, it has some errors like:
"can't be blank" (attribute: email, value: nil)
I want to output the value user that is loaded using factory_girl to see what is going on.

Change your it statement to a do block and then on the first line just log out user:
it "should do something" do
p "#{user.inspect}"
# use expect here
user.email = "valid#email.com"
user.password = "abc123"
expect(user.save).to eq(true)
end

Related

Why does create from factory_girl throw error?

When i don't use factory_girl
I can create objects in my rspec test as
u = User.create(email: 'as#wewe.com', password: 'asdqweqweqe', admin: true, firstname: 'qwe', lastname: 'wer', grade: 5, section: 'w')
expect(u.errors[:email]).to_not be_empty
expect(u.errors[:role]).to_not be_empty
This way i can check for validation error i.e
expect(u.errors[:role]).to_not be_empty
but if i use factory girl
factory :user_no_role, class: User do
email 'asd#we.com'
password 'asasdasdasd'
admin true
firstname 'qwe'
lastname 'wer'
grade 5
section 'w'
end
it 'role should be present' do
u = create(:user_no_role)
expect(u.errors[:role]).to_not be_empty
end
I get the following error
User role should be present
Failure/Error: u = create(:user_no_role)
ActiveRecord::RecordInvalid:
Validation failed: Role can't be blank
does factory_girl create method throw error? if so how can i test for validation error with rspecs as done above? Thanks!
Create will throw validation exceptions, since you are trying to save them immediately.
If you would just build one, like:
u = User.new(...)
u.valid?
=> false
u.errors # is not empty
This should apply to FactoryGirl.build too
it 'role should be present' do
u = build(:user_no_role)
expect(u.errors[:role]).to_not be_empty
end
BTW: User.create will not throw exceptions - but User.create! does.
See the ActiveRecord docs of create! and FactoryGirl docs of build for more infos.

No Method Error while testing with Rspec in Rails

I get a No Method Error while Testing with Rspec. Here is my code for the Test:
scenario 'admin updates a commissionrate' do
create(:assignment, user_id: #user.id, role_id: 1)
sign_in(#user)
commissionrate = create(:commissionrate, rate: 0.05)
visit edit_commissionrate_path(commissionrate)
fill_in 'rate', with: 0.05
click_on 'Eingaben speichern'
expect(page).to have_content('erfolgreich')
end
and this is the factory for the commissionrate:
require 'faker'
FactoryGirl.define do
factory :commissionrate do
commissionrate { Faker::Number.number(0.05)}
end
end
I receive the error: undefined method 'commissionrate=' for #<Commissionrate:0xbc5ac10>
Any ideas where I'm wrong?
I feel you are supposed to assign factory values to your fields/columns in your model. Is commissionrate a field or should you actually rename it as rate? Just check that and see. Also, it would be useful if you can give information such as the model name and the columns.
Seems like you're trying to assign commissionrate, which is not a valid attribute of model commissionrate.
Try changing the line
commissionrate { Faker::Number.number(0.05)}
to
rate { Faker::Number.number(0.05)}
I can see that there is a column rate in commissionrate model in this line commissionrate = create(:commissionrate, rate: 0.05).

FactoryGirl + Rspec custom validation test

I'm pretty new to testing.
I have a custom validation at my Profile model
def birth_date_cannot_be_in_the_future
errors.add(:birth_date, "the birth date cannot be in the future") if
!birth_date.blank? && birth_date > Date.today
end
At my factories.rb
sequence(:email) {|n| "person-#{n}#example.com"}
factory :user do
email
password 'password'
password_confirmation 'password'
confirmed_at Time.now
end
factory :profile do
user
first_name { "User" }
last_name { "Tester" }
birth_date { 21.years.ago }
end
At my models/profile_spec.rb
it 'birth date cannot be in the future' do
profile = FactoryGirl.create(:profile, birth_date: 100.days.from_now)
expect(profile.errors[:birth_date]).to include("the birth date cannot be in the future")
expect(profile.valid?).to be_falsy
end
When I run my test I receive the follow message:
Failure/Error: profile = FactoryGirl.create(:profile, birth_date: 100.days.from_now)
ActiveRecord::RecordInvalid:
The validation fails: Birth date the birth date cannot be in the future
What am I doing wrong?
There's a matcher just for catching errors. Without having tested, I'm assuming you can go:
expect { FactoryGirl.create(:profile, birth_date: 100.days.from_now) }.to raise_error(ActiveRecord::RecordInvalid)
Another approach though is to include the shoulda gem, which has the allow_value matcher. This lets you do something more like this in your model spec:
describe Profile, type: :model do
describe 'Birth date validations' do
it { should allow_value(100.years.ago).for(:birth_date) }
it { should_not allow_value(100.days.from_now).for(:birth_date) }
end
end
Generally you wont need FactoryGirl at all when you're testing things like validations. They become super useful in controller tests though.
Just put assertions for your model in a model spec, which tests your model code directly. These usually live in spec/models/model_name_spec.rb There are convenient shoulda matchers for a bunch of common model stuff:
describe SomeModel, type: :model do
it { should belong_to(:user) }
it { should have_many(:things).dependent(:destroy) }
it { should validate_presence_of(:type) }
it { should validate_length_of(:name).is_at_most(256) }
it { should validate_uniqueness_of(:code).allow_nil }
end
It should be using build instead of create
Also profile.valid? should be called before checking profile.errors
it 'birth date cannot be in the future' do
profile = FactoryGirl.build(:profile, birth_date: 100.days.from_now)
expect(profile.valid?).to be_falsy
expect(profile.errors[:birth_date]).to include("the birth date cannot be in the future")
end

Rails + Rspec: How to stub a record so that it's marked as invalid?

I have a Purchase model with a method:
def set_status_to_in_progress!
self.update_attributes!(status: IN_PROGRESS)
end
And a failing rspec test:
context "self is invalid" do
it "raises an error" do
purchase = Purchase.new
purchase.stub(:valid?).and_return(:false)
expect { purchase.set_status_to_in_progress! }.to raise_error
end
end
which returns
Failures:
1) Purchase#set_status_to_in_progress! self is invalid raises an error
Failure/Error: expect { purchase.set_status_to_in_progress! }.to raise_error
expected Exception but nothing was raised
# ./spec/models/purchase_spec.rb:149:in `block (4 levels) in <top (required)>'
I thought stubbing valid? would be enough to make the ActiveRecord update_attributes! method raise an error? How would I make it raise?
Try changing :false to false
purchase.stub(:valid?).and_return(false)
or
purchase.should_receive(:valid?).and_return(false)
otherwise you can stub any instance of Purchase
Purchase.any_instance.should_receive(:valid?).and_return(false)
This is the DEFINITIVE guide to successfully test validation errors when there is no way to simulate with real validations on your model. In my case the SupportRequest model does not have any validation but I want to test like there is one, so what I did first was create a double then make it so it return false when trying to update, then added errors to the record and last test that the records were there. :)
describe "with invalid data" do
before do
the_double = instance_double("SupportRequest", id: support_request.id)
active_model_errors = ActiveModel::Errors.new(the_double).tap { |e| e.add(:description, "can't be blank") }
allow_any_instance_of(SupportRequest).to receive(:update_attributes).and_return(false)
allow_any_instance_of(SupportRequest).to receive(:errors).and_return(active_model_errors)
put "/api/support_requests/#{support_request.id}",
params: {
data: {
type: "support-requests",
attributes: {}
}
},
headers: authenticated_header(support_agent)
end
it "should not create a new support_request" do
expect_json(errors: [
{
source: {
pointer: "/data/attributes/description"
},
detail: "can't be blank"
}
])
end
it "should return status code (422)" do
expect_status(422)
end
end

RSpec shows 2 different instances of model after save

In debt management app I test the behavior, when user borrow money (create expense_debt) and then return them (create income_debt), app updates expense_debt.returned to true.
My debt_rspec.rb:
require 'rspec'
describe Debt do
user = FactoryGirl.create(:user)
let(:expense_debt) { FactoryGirl.build(:expense_debt, user: user) }
let(:income_debt) { FactoryGirl.build(:income_debt, user: user) }
subject { income_debt }
it 'update expense_debt.returned' do
expense_debt.save
income_debt.save
expect(expense_debt.returned).to be_true
end
end
This test fails, but in development everything works ok.
Then I've found that expense_debt and Debt.first has different values of returned. And if I rewrite test to:
it 'update expense_debt.returned' do
expense_debt.save
income_debt.save
expect(Debt.first.returned).to be_true
end
it passes.
I can't understand, why they are not the same.
# This is expense_debt
#<Debt id: 1, ..., returned: false, ...>
# And this is Debt.first
#<Debt id: 1, ..., returned: true, ...>
Can somebody explain this behavior of RSpec?
may be it is using the cache version. Try this
expect(expense_debt.reload.debt_returned).to be_true

Resources