default rails nested controller rspec testing - ruby-on-rails

So, I have a controller nested within 2 others
here's an example route
products/123/conditions/321/inventories/121
and the controllers are nested like this as well, so I'm trying to figure out what to stub out in my tests.
Product.should_receive(:find) does not get called. I'm wondering what will be the first thing called so I can begin to stub it.
I could find this out if I had a way to take a class and listen to all methods called on it. Is there a way to do that? I tried redefining Products to nil, so that any method called would throw an error, it didn't seem to work.

I could find this out if I had a way to take a class and listen to all methods called on it. Is there a way to do that?
Well, you could replace the class with a mock:
Product = mock
From that point on, any method called on Product should generate a failure, e.g. "Mock received unexpected message :find".

Related

rails before_create not called when creating an object with Object.const_get().new

I have a single table inheritance mechanism and a controller method that creates objects based on a text type.
From my controller:
tile = Object.const_get(tile_data[:type]).new(params_from_tile(tile_data))
tile.save
inside my model base class I have several before create hooks:
before_create :set_handle, :upload
It appears none of my hooks are firing, does it have something to do with my use of Object.const_get to create my objects?
Edit: I've managed to work around this by not using Object.const_get().new now I'm just invoking my Tile.new directly, and there does not appear to be any negative repercussions, so yeah.
Theoretically, there is no difference how you access the class, both of these would behave exactly same:
Tile.new(params_from_tile(tile_data))
and
Object.const_get("Tile").new(params_from_tile(tile_data))
Your seeing bad behaviour may have to do with some other small thing missing.
May be tile_data[:type] in your example pointing to something else, did you make sure Tile record gets saved without callback. Can you try with Object.const_get("Tile") and see what happens.
I've changed this to invoke the baseclass directly:
Tile.new(params_from_tile(tile_data))
And now my hooks are being called as expected, so I'm not sure why this behaves this way, and would appreciate a better answer from someone who knows, but it appears that the answer is that using Object.const_get().new to create an object skips all hooks. On a side note, Invoking create on the baseclass with just a type attribute will still cause subclass hooks to fire, So thats nice.

How can I get access to the class instance that is rendering my view in a test?

I'm trying to test a rake task which scrapes my site and pushes the content to an elasticsearch server; the task works fine. However the test is failing because in one view I randomly pick some values like this:
[:breast,:ovarian][rand(2)]
(rand * 4)-2
rand(Date.new(2006)..Time.now.to_date)
Which means I need to stub rand. In order to stub rand I need access to the class-instance that is calling it, which in this case is whatever class is rendering my view. Calling puts self.class Just returns Class and an id, so how can I get ahold of the instance in order to stub it?
I could pass these values into the view from the controller as instance variables, if getting ahold of the controller would be easier.
long story short: it would be better to extract the offending logic and place it in a helper. This way you will be able to stub it easily, and even unit-test it if needed. Moreover this improves the overall quality of your code (no logic should belong to the view).
also, rand is a method from Kernel, so it is already "stubable"

How do i stub a method/attribute of all instances of a class based on another attribute value?

Either this doesn't exist or i am looking at this the wrong way.
In rspec, I want to stub a method/attribute of all the instances of a class but only if that instance follows a certain condition, for example:
the following code will stub all posts with given comments:
Post.any_instance.stub(:comments).and_return([comment1, comment2])
but I only want to stub comments if the post is published, otherwise i want a blank comments array.
Is there any way I can do something like this:
Post.any_instance.stub(:comments) do |post|
post.published ? [comment1,comment2] : []
end
I have seen solutions where you send an argument to the stubbed method and based on argument value you can return different values, but that's not the case here.
The code you've included should work fine. Stubbing with a block is documented in https://relishapp.com/rspec/rspec-mocks/v/3-3/docs/old-syntax/any-instance#block-implementation-is-passed-the-receiver-as-first-arg, although it's deprecated now in favor of the new methods described at https://relishapp.com/rspec/rspec-mocks/v/3-3/docs/working-with-legacy-code/any-instance

Trying to stub chained methods for a model in Rspec 3

I am really new to Rspec and tried to find my answer, but it keeps on pointing me to use stub_chain, but it seems like it is deprecated on Rspec3. I have the following I am trying to stub:
active_automation = Client.automation_active_status.new_client
Where Client is my model and automation_active_status is the following in my Client model
scope :automation_active_status, -> { where(automation_status: true) }
new_client is an attribute I want to call to further filter my result
I tried to implement stub_chain but that did not work. My goal is to get the following to work:
Client.any_instance( black_box_I_can_not_figure_out ).returns[something]
Thank you.
I believe you might be looking for allow and receive_message_chain.
allow(Client).to receive_message_chain(:automation_active_status, :new_client) { [thing_to_return] }
This will stub the methods allowed it, and return whatever comes out of the block you pass it. Hope it helps.
Client.stub_chain(:automation_active_status, :new_client).and_return([thing-to-return]) should get you where you're trying to get.
Additionally, any_instance should be used on instances of a class. For instance, Client.first would be an instance, but Client is the class (and you can stub directly on it).

Rails best practices : Public methods in model

I'm a newbie and I'm wondering if my app is going to fail is a near future because I don't understand all subtleties of Rails. So I prefer to ask you :-)
I have a User and a Product model, and I want to create a method that could be used like that :
#user.take!(product)
So I wrote in my User model the following line :
def take!(product)
product.owner = self
end
But if I do that in the private section of my model, it doesn't work. And if I do that in the public section, I don't know if it's recommended. I'm asking myself if it would be better to do something like that in a controller, or in a helper...
Can you enlighten me ?
Writing public methods is fine if they need to be public, it's not a hanging offence or anything like that. The method you describe shouldn't be on the user though - there's no need to put product logic inside the user model, and it's definitely bad to change the product by passing it into a user method. It's also a very short method and so there isn't really very much to be gained by putting it into a method - it just means that if I see take! then I have to go and find where it's defined in order to understand it. You should also only use ! at the end of methods that either might raise an exception or alter the object they are called on.
Putting this logic in the controller would be fine and much clearer, but in general there's nothing wrong with public methods if they can't be private.

Resources