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
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users