David James
2007-Sep-16 05:20 UTC
[rspec-users] How far to go with ActiveRecord unit tests without hitting the database?
I''m currently try to push my limits a little bit with some of my unit testing -- trying to avoid saving ActiveRecord objects to the database and take advantage of mock/stub objects. How far should I expect to get in this direction? From what I can tell, ActiveRecord seems to fight me when it comes to associations. In other words, many associations seem to require database queries. Have others had success in mocking/stubbing associations? In particular, have you had luck with has_many :through? -David -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20070916/bf84a106/attachment.html
Pat Maddox
2007-Sep-16 05:43 UTC
[rspec-users] How far to go with ActiveRecord unit tests without hitting the database?
On 9/15/07, David James <davidj503 at gmail.com> wrote:> I''m currently try to push my limits a little bit with some of my unit > testing -- trying to avoid saving ActiveRecord objects to the database and > take advantage of mock/stub objects. > > How far should I expect to get in this direction? From what I can tell, > ActiveRecord seems to fight me when it comes to associations. In other > words, many associations seem to require database queries. > > Have others had success in mocking/stubbing associations? In particular, > have you had luck with has_many :through? > > -David > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >You can''t assign mock objects to associations. You have to do stuff like @employee = Employee.new :name => "Pat" @mock_company = mock_model(Company, :name => "BigCo") @employee.stub!(:company).and_return @mock_company As far as has_many through, again you just mock out the association proxy. So if you''ve got class User < ActiveRecord::Base has_many :rentals has_many :videos, :through => :rentals end and you want to do some testing with videos, you''d have @videos_proxy = mock("videos proxy") @user.stub!(:videos).and_return @videos_proxy You don''t deal with the fact that rentals plays in anywhere, at least if you''re trying to isolate from the database. Pat
Tom Stuart
2007-Sep-16 08:05 UTC
[rspec-users] How far to go with ActiveRecord unit tests without hitting the database?
On 16 Sep 2007, at 06:43, Pat Maddox wrote:> You can''t assign mock objects to associations.Actually I haven''t had any trouble with e.g. @company = Company.new :name => ''BigCo'' @mock_employee = mock_model(Employee, :name => ''Pat'', :[]= => true, :save => true) @company.employees << @mock_employee (To be followed by, for example, @mock_employee.should_receive (:destroy); @company.destroy.) Is that unacceptable? Cheers, -Tom
Scott Taylor
2007-Sep-16 17:59 UTC
[rspec-users] How far to go with ActiveRecord unit tests without hitting the database?
On Sep 16, 2007, at 1:20 AM, David James wrote:> I''m currently try to push my limits a little bit with some of my > unit testing -- trying to avoid saving ActiveRecord objects to the > database and take advantage of mock/stub objects. > > How far should I expect to get in this direction? From what I can > tell, ActiveRecord seems to fight me when it comes to > associations. In other words, many associations seem to require > database queries. > > Have others had success in mocking/stubbing associations? In > particular, have you had luck with has_many :through? >I would highly suggest taking a look at Jay Fields blog. The examples use Test::Unit, but that shouldn''t discourage you :). In one of his posts he stubs/mocks out the class used for a database column in ActiveRecord (ActiveRecord::ConnectionAdapter::Column). I, personally, though, prefer to go the opposite way. Integration Testing and Unit Testing are on a continuum, and in my opinion, the only advantage of going the Jay Field''s way (that is, the traditional mocks/stubs approach - this is the one end of the continuum) is speed. The truth is that Rails was not developed by a BDD''er, and the coupling is too tight. When you test rails the way you would test anything else, you''ll soon realize how tight this coupling is, because you will spend more time debugging rails then debugging your own app. Just as an example, the other day I had a method call in an ActiveRecord class which did something like this: Tag.find_or_create_by_name("Summer") and I had the following expectation: Tag.should_receive(:create).with(:name => "Summer") So where was my test failing? It wasn''t in the logic of *my* code. It was in how ActiveRecord dealt with this dynamic finder. The docs say this: Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer") So I ran my debugger. Guess what! The call was to Tag.create("name" => "Summer"), not Tag.create(:name => "Summer"). (The difference was that "name" was a string, not a symbol). So - if you can afford to spend time debugging the internals of rails (or if you know rails inside and out) , then yes - go right ahead and have incredibly speedy unit tests. But otherwise, what exactly are you testing? Sorry for the rant, Scott Taylor
David James
2007-Sep-18 13:23 UTC
[rspec-users] How far to go with ActiveRecord unit tests without hitting the database?
> > "But otherwise, what exactly are you testing?" >That''s a great point. I don''t want to be specing Rails if it is not easily separable for testing already. In theory, I would like to have true unit tests for my models. However, in practice, some of my models are coupled to some degree. Generally speaking, I prefer not to mock/stub Rails internals to test my app. I don''t find mocking/stubbing *my* code -- that is fine with me. But the internals of Rails are *not* what I''m trying to test. In particular, I found that mocking/stubbing with has_many :through was far from simple. I fired up ruby-debug (highly recommended!) and everything, but realized it was too much effort and too little time. In this sense, my experience is probably similar to Scott''s (above post). My Goal model, for example, checks to see how many Contributions it has. It uses a has_many :through (hmt) association. Despite the helpful words of some posters in this thread, I couldn''t find a way to effectively spec the hmt association. By the time I had stubbed out enough to make it a true unit test, I was no longer testing what I wanted to test -- namely, that the association itself. Maybe this was a wake up call that there was not point in trying to test that at a unit level? In any case, I''ve got some conditional logic that connects with the association, so I definitely need to test that, at least. In the end, I just tested the "interaction" between models. In this folder, I test how models in my app work together. I didn''t think these specs fit under ''model'' because they are testing more than one model. So I put them in a new directory called ''model_integration'' folder under the ''spec'' directory. I came up with this name on my own intuition, and I can''t blame anyone else if this is a bad idea. I wasn''t sure if ''model_interaction'' would have been a better name, but from what I gather, ''interaction'' is a special term when it comes to testing. Suggestions? -DJ -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20070918/43513278/attachment.html