-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Help - how to mock 'deep' methods? #55
Comments
Can you expound, please? |
Sorry, fat-fingered return while in the title, so github posted it early! :) |
So... these sorts of things are all too common in ActiveRecord. Because of how painful it's going to be using any mocking library, I would actually not mock this and instead use a more functional test to test that this query is doing the right thing. (I would also move it to the model layer that way you are also testing it on the model layer and not the controller level) However if you really want to mock this then what you basically need to do is mock every link in the chain. I'm assuming that mock_user = stub!
stub(User).find { mock_user }
mock_relation2 = mock!.destroy_all
mock_relation1 = mock!.where('user_id=?', mock_user) { mock_relation1 }
mock_items = mock!.joins(:item) { mock_relation1 }
mock_group = mock!.group_items { mock_items }
stub(Group) { mock_group } The missing step for you, I think, is mock_user = Object.new
stub(User).find(some_id) { mock_user }
mock_relation2 = Object.new.tap do |object|
mock(object).destroy_all
end
mock_relation1 = Object.new.tap do |object|
mock(object).where('user_id=?', mock_user) { mock_relation1 }
end
mock_items = Object.new.tap do |object|
mock(object).joins(:item) { mock_relation1 }
end
mock_group = Object.new.tap do |object|
mock(object).group_items { mock_items }
end
stub(Group).find { mock_group } Anyway, as you can see, this sort of thing is super unwieldy and is oftentimes not worth the trouble. But that's up to you to decide :) |
Wow, thanks so much for the deep reply, I appreciate it. Too bad we can't do something like this:
Which could create a chain of anonymous stubs automatically & expect them to get called.. Or even making it do the splitting, which gets really elegant: mock Group, "find.group_items.joins.where.destroy_all" I've been writing some angularjs lately and was hoping to bring their testing ideas back into my rails programming, but models in angular are always such short chains they're easier to mock... |
Yeah, RSpec actually has a method similar to what you're talking about called expect(Group).to receive_message_chain('find.group_items.joins.where.destroy_all') Note that if you were interested in the arguments passed to these methods, you'd still have to make separate assertions to check for that. So I'm not sure you'd gain that much. Still, I suppose, RR could do something similar, and at the same time also allow you to keep expectations on individual method calls: mock_user = stub!
mock(User).find(some_id) { mock_user }
mock_chain(Group)
.find(some_id)
.group_items
.joins(:items)
.where('user_id=?', mock_user)
.destroy_all I didn't mention this before, but you can actually kind of get something like that today: mock_user = stub!
mock(User).find(some_id) { mock_user }
mock(Group).find(some_id) do
mock!.group_items.
mock!.joins(:items).
mock!.where('user_id=?', mock_user).
mock!.destroy_all
end That is, |
I've been a fan of rr, but have been unable to use it for much besides unit tests (since the object is already created for those).
For example, my controller calls this
I want to create an expectation that this method is called, since it some cases it seems to not be happening. Any suggestions how to do something this convoluted? Ultimately should I be mocking "ActiveRecord::Associations::CollectionProxy", as that seems crazy...
The text was updated successfully, but these errors were encountered: