On Apr 24, 2011, at 6:25 PM, Rodrigo Rosenfeld Rosas wrote: > Em 24-04-2011 12:09, David Chelimsky escreveu: >> On Apr 24, 2011, at 9:29 AM, Rodrigo Rosenfeld Rosas wrote: >> >>> I've started a fresh rails app with Employee belongs_to Company. >>> >>> Here is the spec: >>> >>> describe Employee do >>> example "stub should work with find(id)" do >>> company = mock_model Company >>> Company.stub!(:find).with(company.id).and_return company >>> employee = Employee.new company_id: company.id >>> employee.company.should == company >>> end >>> end >> This ^^ is specifying Rails' behavior. There is a rather large test suite >> that does this already, so I'd recommend avoiding this sort of example in a >> model spec. Use-case specifics aside ... > > Yes, I know, I just wanted to make my point. This is not a real test case. > >>> And here is the result: >>> >>> Failures: >>> >>> 1) Employee stub should work with find(id) >>> Failure/Error: employee.company.should == company >>> <Company(id: integer, name: string, created_at: datetime, updated_at: >>> datetime) (class)> received :find with unexpected arguments >>> expected: (1001) >>> got: (1001, {:conditions=>nil}) >>> >>> Writing any of the following is not elegant: >>> >>> Company.stub!(:find).with(company.id, conditions: nil).and_return company >>> or >>> Company.stub!(:find).with{|id, *args| id == company.id }.and_return >>> company # find could be called like Company.find(1, 2, 3) which should >>> return an array instead >>> or >>> Company.stub!(:find).with{|id, *args| id == company.id&& (args.size == 0 >>> || (args.size == 1&& args[0].is_a?(Hash)) }.and_return company >>> >>> So, it would be great to add some syntax to rspec-rails like: >>> >>> Company.stub!(:find).with_id(company.id).and_return company >>> >>> or >>> >>> Company.stub_find_with!(company.id).and_return company >>> >>> I'm not sure yet what changes would be better, but the current >>> implementation makes it very hard to read specs like this that are so >>> common when mocking models. >>> >>> What do you think? >> Totally agree that this _should_ be easy. The problem with this approach is >> that it would add a dependency from rspec-rails on Rails' internals. That >> means that if/when the internals change, rspec-rails would break. > > Not exactly, only if the Rails API changes which I don't think it will happen > for find in the near future. > >> What about a more generalized `with` method that only constrains the first n >> arguments? >> >> Company.stub(:find).with_args_including(company.id).and_return company >> >> This would work with find(company.id) or find(company.id, anything_else), >> but not find(anything_else, company.id). This doesn't guarantee solving the >> problem, but it reduces the risk of future breakage because the likelihood >> of active record changing that first argument to find is very small. >> >> Thoughts? > > The problem with this approach is that I don't want Company.find(1, 2) to > return Company.find(1), since it should return [Company.find(1), > Company.find(2)]... > > Actually, I was thinking about something like this: > > company = mock_active_model(Company) > Company.find(company.id).should == company > > I mean, whenever a mock for some ActiveModel or ActiveRecord instance is > created, the class should also be stubbed to return that instance on find > too. There are basically two find alternatives: find(id[, options]) and > find(id1, id2...[, options]). These haven't changed as long as I can > remember, so I think it would be safe to include something like that on > rspec-rails. > > Do you agree? > > Best regards, Rodrigo.
I definitely agree that it would be a nice to have if we could make it work simply and reliably. That said, rspec-rails-1 has a long history of me having to drop everything and do an urgent release immediately following a rails-2 release because internals that we never thought would change did. rspec-rails-2, however, has survived 7 rails-3 releases unscathed, and I intend to keep it that way. One major reason for this is a severely reduced dependency on rails internals. In terms of the API, how would you expect this to work?: company = mock_active_model(Company, :id => 1) Company.find(1, 2) or this: company = mock_active_model(Company, :id => 1, :published_on => 8.days.ago) Company.find(1, :conditions => [ "published_on > ?", 7.days.ago ] Etc. Another concern is that named scopes defined with ARel do not go through the find method, so this approach would not survive a refactoring from ActiveRecord finders to scopes defined with ARel relations. Even if we can find "the spot" through which all queries go, I would be resistant to depending on its stability. There is a similar thread, right now, btw, in an issue raised on rspec-rails: https://github.com/rspec/rspec-rails/issues/358. Feel free to review my comments there and add yours. I'm not closed to any of these ideas, but I am opposed to introducing brittle dependencies, or new APIs that open the door for a battery of new expectations/requirements. Cheers, David _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users