Why did post fail in my Rails functional test? - ruby-on-rails

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

Related

Reloading an object not working in rspec

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)

RSpec results and Rails Console behaving differently

Here is my Lesson model:
before_create :set_sequence
def set_sequence
maxseq = Lesson.where(:course_id => self.course_id).maximum("sequence")
if (maxseq.nil?)
maxseq = 0
end
self.sequence = maxseq + 1
end
when I run rspec the following test fails:
it "validate sequence is setup" do
lesson = Lesson.create(:title => "Testing", :description => "Testing", :course_id => 1)
lesson.sequence.should_not eql nil
end
However when T test this through rails console the Lesson object is created successfully and with the correct sequence. Any ideas why?
lesson.sequence.should_not be_nil is the correct way to test for nil, as far as I know. Have you tried that?
Any validations you've got on Lesson could be silently aborting the create before your callback gets called. Change it to create! in the spec to check it.
FactoryGirl first initializes object with no parameters, and then assigns parameters one by one. The callback in your model probably would not work in this case. So you can try to change FactoryGirl's behavior by adding
initialize_with { new(attributes) }
to the Lesson's factory. I'm not sure it will help though. Testing callback behavior in Rails is tricky.

FactoryGirl not giving useful validation errors

I'm using FactoryGirl for my fixtures and am finding that it's not really producing useful validation errors.
I always get the message for activerecord.errors.models.messages.record_invalid.
Not sure what further details are needed to help diagnose this. This makes it an excruciatingly slow process to track each error down.
Example factory:
Factory.define :partner do |partner|
partner.sequence(:username){ |n| "amcconnon#{n}" }
partner.first_name "Bobby Joe"
partner.last_name "Smiley"
partner.sequence(:email){ |n| "bob{n}#partners.com" }
partner.phone_number "5557 5554"
partner.country_id 75
partner.password "password"
partner.password_confirmation "password"
end
Then Factory(:partner) => "ActiveRecord::RecordInvalid Exception: Looks like something went wrong with these changes"
The actual problem is of course the email sequence doesn't use n properly and there is a unique validation on email. But that's for illustrative purposes.
rails => 3.2.2
factory_girl 2.6.1
Any other deets needed to help diagnose this?
(Note: edited this just to add an easier to read factory)
EDIT:
As per bijan's comment: "What exactly am I trying to do."
Trying to run "rspec spec". I would like when I use a factory like Factory(:partner) in this case for the error message when that fails to contain the same error I would get from Partner.new({blah...}).valid? then looked at the validation failures.
I'm not sure if this is exactly the same problem you were having, but it's a problem I was having with validation error messages not being very useful and I thought it could be useful to others searching for this problem. Below is what I came up with. This could be modified to give different info too.
include FactoryGirl::Syntax::Methods
# Right after you include Factory Girl's syntax methods, do this:
def create_with_info(*args, &block)
create_without_info(*args, &block)
rescue => e
raise unless e.is_a? ActiveRecord::RecordInvalid
raise $!, "#{e.message} (Class #{e.record.class.name})", $!.backtrace
end
alias_method_chain :create, :info
Then, when you use create :model_name, it will always include the model name in the error message. We had some rather deep dependencies (yes, another problem), and had a validation error like, "name is invalid"... and name was an attribute on a few different models. With this method added, it saves significant debugging time.
I think the key point here is that when you're setting up a test you need to make sure that the code you're testing fails at the point that you set the expectation (i.e. when you say "should" in Rspec). The specific problem with testing validations is of course that the validation fails as soon as you try to save the ActiveRecord object; thus the test setup mustn't invoke save.
Specific suggesions:
1) IMO Factory definitions should contain the minimum information required to create a valid object and should change as little as possible. When you want to test validations you can override the specific attribute being tested when you instantiate the new test object.
2) When testing validations, use Factory.build. Factory.build creates the ActiveRecord instance with the specified attributes but doesn't try to save it; this means you get to hold off triggering the validation until you set the expectation in the test.
How about something like this?
it "should fail to validate a crap password" do
partner_with_crap_password = Factory.build(:partner, :password => "crap password")
partner_with_crap_password.should_not be_valid
end

Help understanding this RSpec test

I'm trying to firm up my understanding of why this actually works
My application is just the default scaffold for the purposes of this experiment.
it "creates a record and redirects with success notice" do
Project.any_instance.stubs(:valid?).returns(true) #ensure action behaves as if #project was valid
post :create, :format => :js
assigns(:project).errors.should be_empty #ensures that #project has no errors
flash[:notice].should_not be_nil #ensure a flash notice is set
response.should redirect_to(projects_path) #ensure appropriate redirection
end
end
In the controller the save method is called on #project. Here is where I become unsure. The object will save because the valid? method has been stubbed to return true, even if the object is not valid. But if the object is not valid how can the save be successful?
Rails is also calling your valid? stub internally before saving, so it proceeds with the save as normal because it thinks nothing is wrong.
On the other hand if you had, say, a UNIQUE index on a field in a MySQL database, a duplicate row there could still prevent the record from being saved, even though you stubbed valid?. Two possible layers of validation.

Stubbing named_scope in an RSpec Controller

I haven't been able to find anything for a situation like this. I have a model which has a named scope defined thusly:
class Customer < ActiveRecord::Base
# ...
named_scope :active_customers, :conditions => { :active => true }
end
and I'm trying to stub it out in my Controller spec:
# spec/customers_controller_spec.rb
describe CustomersController do
before(:each) do
Customer.stub_chain(:active_customers).and_return(#customers = mock([Customer]))
end
it "should retrieve a list of all customers" do
get :index
response.should be_success
Customer.should_receive(:active_customers).and_return(#customers)
end
end
This is not working and is failing, saying that Customer expects active_customers but received it 0 times. In my actual controller for the Index action I have #customers = Customer.active_customers. What am I missing to get this to work? Sadly, I'm finding that it's easier to just write the code than it is to think of a test/spec and write that since I know what the spec is describing, just not how to tell RSpec what I want to do.
I think there's some confusion when it comes to stubs and message expectations. Message expectations are basically stubs, where you can set the desired canned response, but they also test for the call to be made by the code being tested. In contrast stubs are just canned responses to the method calls. But don't mix a stub with a message expectation on the same method and test or bad things will happen...
Back to your question, there are two things (or more?) that require spec'ing here:
That the CustomersController calls Customer#active_customers when you do a get on index. Doesn't really matter what Customer#active_customers returns in this spec.
That the active_customers named_scope does in fact return customers where the active field is true.
I think that you are trying to do number 1. If so, remove the whole stub and simply set the message expectation in your test:
describe CustomersController do
it "should be successful and call Customer#active_customers" do
Customer.should_receive(:active_customers)
get :index
response.should be_success
end
end
In the above spec you are not testing what it returns. That's OK since that is the intent of the spec (although your spec is too close to implementation as opposed to behavior, but that's a different topic). If you want the call to active_customers to return something in particular, go ahead and add .and_returns(#whatever) to that message expectation. The other part of the story is to test that active_customers works as expected (ie: a model spec that makes the actual call to the DB).
You should have the array around the mock if you want to test that you receive back an array of Customer records like so:
Customer.stub_chain(:active_customers).and_return(#customers = [mock(Customer)])
stub_chain has worked the best for me.
I have a controller calling
ExerciseLog.this_user(current_user).past.all
And I'm able to stub that like this
ExerciseLog.stub_chain(:this_user,:past).and_return(#exercise_logs = [mock(ExerciseLog),mock(ExerciseLog)])

Resources