What is the correct way to mock out the errors on a Rails model? I''m assuming i need to say @mock_thing = mock_model(Thing) @mock_thing_errors = mock("errors") @mock_thing_errors.should_receive(:full_messages).and_return("An error") @mock_thing.should_receive(:errors).and_return(@mock_thing_errors) Just wanted to check the best practice on this kind of thing and how other people handle it. Cheers, David
> What is the correct way to mock out the errors on a Rails model? > > I''m assuming i need to say > > @mock_thing = mock_model(Thing) > @mock_thing_errors = mock("errors") > @mock_thing_errors.should_receive(:full_messages).and_return("An > error") > @mock_thing.should_receive(:errors).and_return(@mock_thing_errors) > > Just wanted to check the best practice on this kind of thing and how > other people handle it.Don''t know if this is the best way, but I do this sort of thing.... thing = Thing.create(:a_required_attribute => nil) thing.should have_at_least(1).errors_on(:a_required_attribute) I don''t think you need to mock the error handling code itself, you just want to check that the result of passing an invalid attribute to a model results in an error on that attribute. Hope this makes sense Cheers Rupert
I''m doing this and overriding the last line if I want errors, but yours should also work if you want a message. Dunno if mine is more or less "correct". @thing = mock_model(Thing, :title => ''my mock title'') @thing.should_receive(:errors).any_number_of_times.and_return (@errors) @errors = mock(''errors'') @errors.should_receive(:size).any_number_of_times.and_return(0) On Jun 24, 2007, at 6:22 AM, David Smalley wrote:> What is the correct way to mock out the errors on a Rails model? > > I''m assuming i need to say > > @mock_thing = mock_model(Thing) > @mock_thing_errors = mock("errors") > @mock_thing_errors.should_receive(:full_messages).and_return("An > error") > @mock_thing.should_receive(:errors).and_return(@mock_thing_errors) > > Just wanted to check the best practice on this kind of thing and how > other people handle it. > > Cheers, > > David > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-usersSteve Ross sross at calicowebdev.com http://www.calicowebdev.com -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20070624/3274243c/attachment.html
Rupert: I assume David wants to do this because he wants to pass a model object with errors to a view or a controller or something to make sure it reacts properly to validation errors. SImplest case: @mock_thing = mock_model(Thing, :errors => mock_model(Errors, :full_messages => ["An error"])) Hey David and Aslak: I wish I could just say "mock(Errors, :full_messages => ["An error"])", since I never use the :null_object option on mock(), and I think it would be better to just have a separate method, "null_object()", for that case anyway. Is there a reason why that''s a bad idea? If you have lots going on in your mock_model call, you might want to split this out into a separate stub! call: @mock_thing = mock_model(Thing, etc. etc.) @mock_thing.stub!(:errors).and_return(mock_model(Errors, :full_messages => ["An error"])) If I needed more methods implemented on Errors than just :full_messages, then I would rather just use a real Errors object. I don''t think you lose anything by doing this, especially since Errors is a library class and not one you own. In fact I think you gain something by using the real interface to this library object instead of a mocked one (to give a more extreme example, it''s better to use a real String than a mock String). In this case it''s nice to write a helper method to decorate the mocked model with an Errors object: def mock_model_with_errors(klass, stubs = {}) model = mock_model(klass, stubs) model.stub!(:errors).and_return(ActiveRecord::Errors.new(model)) model end and in the example setup: before(:each) do ... assigns[:thing] = mock_model_with_errors(Thing, :a_method => "whatever", etc.) assigns[:thing].errors.add_to_base("An error") end Following the Law of Demeter helps reduce the complexity of mocking. In any situation where Rails frustrates your ability to follow the law (like ActiveRecord Errors), thereby turning what should be one line of mock setup into several, I often like to write test helper methods like the above. You do run the risk of making things less clear in your test, however, so tread carefully. I also have this problem mocking ActiveRecord association proxies, where I need to mock things like client.invoices.find(:conditions => "whatever") in a controller or something. Moses On 6/24/07, David Smalley < david.smalley.lists at googlemail.com> wrote:> > What is the correct way to mock out the errors on a Rails model? > > I''m assuming i need to say > > @mock_thing = mock_model(Thing) > @mock_thing_errors = mock("errors") > @mock_thing_errors.should_receive(:full_messages).and_return("An error") > @mock_thing.should_receive(:errors).and_return(@mock_thing_errors) > > Just wanted to check the best practice on this kind of thing and how > other people handle it. > > Cheers, > > David > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20070624/66612444/attachment-0001.html
Thanks everyone for your replies on a Sunday! On 24 Jun 2007, at 18:45, Moses Hohman wrote:> Rupert: I assume David wants to do this because he wants to pass a > model object with errors to a view or a controller or something to > make sure it reacts properly to validation errors.Bingo, you got that right in one.> SImplest case: > > @mock_thing = mock_model(Thing, :errors => mock_model > (Errors, :full_messages => ["An error"]))Cool - I''ll start looking into this right now. It''ll tidy up my current setup code. Thanks guys, David -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20070624/16147c9f/attachment.html
On 6/24/07, David Smalley <david.smalley.lists at googlemail.com> wrote:> > > SImplest case: > > @mock_thing = mock_model(Thing, :errors => mock_model(Errors, > :full_messages => ["An error"])) > >I left out something, that should actually be: @mock_thing = mock_model(Thing, :errors => mock_model(ActiveRecord::Errors, :full_messages => ["An error"])) Moses -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20070624/5f457e9d/attachment.html
I think having the errors collection not block specs from working initially should be a part of mock_model. Many, if not most, views rely on that collection''s existence, so it could be considered a common case. Is this a candidate for including in mock_model by default, allowing the user to override to test certain error or success cases? Steve On Jun 24, 2007, at 4:48 PM, Moses Hohman wrote:> > > On 6/24/07, David Smalley <david.smalley.lists at googlemail.com> wrote: > >> SImplest case: >> >> @mock_thing = mock_model(Thing, :errors => mock_model >> (Errors, :full_messages => ["An error"])) > > I left out something, that should actually be: > > @mock_thing = mock_model(Thing, :errors => mock_model > (ActiveRecord::Errors, :full_messages => ["An error"])) > > Moses > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-usersSteve Ross sross at calicowebdev.com http://www.calicowebdev.com -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20070624/07293f68/attachment.html