I am trying to test a controller method with the following code:
it "should set an approved_at date and email the campaign's client" do
#campaign = Campaign.create(valid_attributes)
post :approve, id: #campaign.id.to_s
#campaign.reload
#campaign.approved_at.should_not be(nil)
end
However, when I run this test, I get the following error:
Failure/Error: #campaign.reload
ActiveRecord::RecordNotFound:
Couldn't find Campaign without an ID
When I run the analagous lines in the rails console, the reload works and the value is set as I need it to be. Why isn't reload working for me when I run the code in an rspec test?
I solved the problem by switching to FactoryGirl:
#campaign = FactoryGirl.create(:pending_approval_campaign)
#campaign.approved_at.should be(nil)
post :approve, id: #campaign.id.to_s
#campaign.reload
#campaign.approved_at.should_not be(nil)
That works as intended
Two possible places for errors.
object creation. i.e.#campaign = Campaign.create(valid_attributes) Your object may not be created correctly. I suggest you to use create! instead of create in the test so that any error will be thrown.
Controller. When controller expect to find the object with an integer id, you feed it a string. That may also be the problem. I suggest you not to convert the id into string. If for GET, you can do that though not necessary. If for POST, converting to string is wrong.
I would run a test to ensure a Campaign record is actually being created:
#campaign = Campaign.create(valid_attributes)
puts #campaign.id
.reload is the first place in your code that a nil #campaign would flag an error (since you can call .to_s on a nil object)
Related
How could I write a test to find the last created record?
This is the code I want to test:
Post.order(created_at: :desc).first
I'm also using factorybot
If you've called your method 'last_post':
def self.last_post
Post.order(created_at: :desc).first
end
Then in your test:
it 'should return the last post' do
expect(Post.last_post).to eq(Post.last)
end
On another note, the easiest way to write your code is simply
Post.last
And you shouldn't really be testing the outcome of ruby methods (you should be making sure the correct ruby methods are called), so if you did:
def self.last_post
Post.last
end
Then your test might be:
it 'should send the last method to the post class' do
expect(Post).to receive(:last)
Post.last_post
end
You're not testing the outcome of the 'last' method call - just that it gets called.
The accepted answer is incorrect. Simply doing Post.last will order the posts by the ID, not by when they were created.
https://apidock.com/rails/ActiveRecord/FinderMethods/last
If you're using sequential IDs (and ideally you shouldn't be) then obviously this will work, but if not then you'll need to specify the column to sort by. So either:
def self.last_post
order(created_at: :desc).first
end
or:
def self.last_post
order(:created_at).last
end
Personally I'd look to do this as a scope rather than a dedicated method.
scope :last_created -> { order(:created_at).last }
This allows you to create some nice chains with other scopes, such as if you had one to find all posts by a particular user/account, you could then chain this pretty cleanly:
Post.for_user(user).last_created
Sure you can chain methods as well, but if you're dealing with Query interface methods I feel scopes just make more sense, and tend to be cleaner.
If you wanted to test that it returns the correct record, in your test you could do something like:
let!(:last_created_post) { factory_to_create_post }
. . .
it "returns the correct post"
expect(Post.last_post).to eq(last_created_post)
end
If you wanted to have an even better test, you could create a couple records before the last record to verify the method under test is pulling the correct result and not just a result from a singular record.
I'm trying to write a failing Rspec test. The actual test is associated with much longer code, but I narrowed down the problem to the class method it's testing.
Here's the test in Rspec:
context "For '.CASH.' as a stock" do
let!(:cash) { FactoryGirl.create(:stock, symbol: '.CASH.', name: 'cash', status: 'Available') }
describe "When update_stock runs on it" do
it "should still have an 'Available' status" do
# status should be 'Error' and test should fail
Stock.change_to_error
expect(cash.status).to eq('Available')
end
end
end
This is testing a model class method in Stock.rb:
def self.change_to_error
self.all.each do |stock|
stock.status = "Error"
stock.save
end
end
For some reason, this passes. However, if I changed it to use an instance method, it will fail like it should:
If stock_spec.rb changed to instance method:
context "For '.CASH.' as a stock" do
let!(:cash) { FactoryGirl.create(:stock, symbol: '.CASH.', name: 'cash', status: 'Available') }
describe "When update_stock runs on it" do
it "should still have an 'Available' status" do
# status should be 'Error' and test should fail
cash.change_to_error
expect(cash.status).to eq('Available')
end
end
end
And if stock.rb class method turned into an instance method:
def change_to_error
self.status = 'Error'
self.save
end
This would pass. Unfortunately, I have to use a class method instead of an instance method because I want to update all stocks in the DB. "Change_to_error" methods are just there to figure out the problem. Does anyone know why it passes as a class method when it should fail? But it fails correctly when it's using an instance method?
Effectively, what is happening is that the class method does not change the status attribute of 'cash', but the instance method does. I don't know why that is happening.
FYI, I'm using rspec-rails
Solution: Need to put 'cash.reload' after 'Stock.change_to_error' and before the expect line.
When using let! the object is created before the test. Updating the underlying data outside the object causes the instance to be outdated. Calling reload on it forces ActiveRecord to refresh it from the database.
When you use let, RSpec does not call the block until the first time you reference the attribute, in this case, cash. So in your first example, you're running change_to_error on no records at all and then checking the status on cash, a record that gets created on the line with expect. In your second example, the cash object is created, then changed to an error. I'd recommend tailing your log to confirm this (tail -f log/test.log)
If you change to let!, RSpec will create the object before every example is run. Another alternative is to reference cash in your example before calling change_to_error on all records that are created.
In the Ruby application I have a class(Resque Job) that has method that affect the values of a different class when called with the id of the latter class.
class ResqueKlass
def self.perform(id)
obj = EditKlass.find(id)
obj.update(value: 0)
end
end
I want to use rspec to test that this value was indeed changed within method
describe 'Something' do
let(:obj){FactoryGirl.create(:editklass)}
scenario 'Change obj value' do
ResqueKlass.perform(obj.id)
expect(obj.value).to eq(0)
end
end
This test fails where it expect 0 it get the value that was set in the factory girl.
I have also tried not using factory girl create 'obj' with let but that still does not work. I have placed bindings in the ResqueKlass perform method and i can see that the value is being updated.
PS please bear in mind that i am new to Ruby and rspec. These are not the exact classes that i am working with, the reason for that is the actual classes contain some sensitive data.
That happens, because you do not reload that record and therefore your obj still shows the old version.
Try reloading the obj with obj.reload:
describe 'Something' do
let(:obj){FactoryGirl.create(:editklass)}
scenario 'Change obj value' do
ResqueKlass.perform(obj.id)
expect(obj.reload.value).to eq(0)
end
end
I am writing a Rspec for update method of a controller which updates the record in a table T with a given id ID. Here in spec I want to add a case that checks whether any record with ID exists in the table T, and if it does not exist, it should give error like following But being new to RSpec I could not complete it.
it "should give error when record with the given id does not exist" do
pending "case yet to be handled"
end
So if anyone helps me in writing this spec.
You should pass the parameter id and use the correct HTTP verb to send request to update action. i.e
it "should give error when record with the given id does not exist" do
put :update, id: <invalid_id>
# you expectations here
end
How I learned to test my Rails applications, Part 4: Controller spe has some example and more explanation on controller tests.
You could do Model.find and check if this record doesn't exist.
it "should give error when record with the given id does not exist" do
expect { Model.find(<your ID>) }.should raise_error(ActiveRecord::RecordNotFound::Error)
end
When I run a post in my Rails functional test
setup do
post :create, :user => Factory.attributes_for(:user)
end
and it fails, I don't get any feedback as to why. I know that it fails because my assertion to make sure that there's one additional record in the database fails.
I tried to do
setup do
post :create, :user => Factory.attributes_for(:user)
assert_valid #controller.object
end
but object is a protected method.
How can I examine the errors on the model object that results from the post call?
I'm using Shoulda and Factory Girl, but I suspect that doesn't matter.
Add the following assertion:
assert_nil assigns(:user).errors
Which will fail if there were errors saving your object (perhaps a validation), and show you the value of the errors object.
I'm using rails 3.2.13, and it seems like assert_nil doesn't work properly as stated in the previous answer.
This is what worked for me:
assert_empty assigns(:user).errors
I believe this is because even a successful "save" call returns an ActiveRecord:Errors object containing with an Empty hash of "messages" so you could also do this:
assert_empty assigns(:user).errors.messages