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

Helper for injecting locals to view specs #1219

Closed
codebeige opened this Issue Nov 14, 2014 · 14 comments

Comments

Projects
None yet
4 participants
@codebeige

codebeige commented Nov 14, 2014

I the past I was testing view templates that rely on locals to be passed in (e.g. partials for rendering a collection) like this:

let(:item) { double 'Item' }

before do
  allow(view).to receive(:item) { item }
end

Current versions of RSpec verify the existence of partially stubbed methods by default and will fail with the following error:

#<#<Class:0x007f9688803b00> [...] >> does not implement: item

For the discussion of a similar issue when stubbing out helper methods from the controller see also #1076.

As I do not want to disable verification of partially stubbed methods completely, I came up with the following workaround:

let(:item) { double 'Item' }
let(:options) { _default_render_options.merge locals: {item: item} }

it 'renders label' do
  allow(item).to receive(:label) { 'My Item' }
  render options
  expect(rendered).to have_text('My Item')
end

I think it would be nice to have a helper method that hides the internals and allows to call render without arguments as usual. Something similar to assign, but passing the defined locals to each call of render behind the scenes:

let(:item) { double 'Item' }

before do
  assign_locals item: item
end

it 'renders label' do
  allow(item).to receive(:label) { 'My Item' }
  render
  expect(rendered).to have_text('My Item')
end

I am not sure about the naming and the signature of this call, though. An alternative could be something more generic like the following:

stub_local :item, item

or

stub_local :item { item }

It can then also be used to stub out other local method calls from controllers e.a. (example taken from #1076):

stub_local :current_customer, customer

This allows to keep partial double verification for view examples enabled while providing a pragmatic and explicit way to deal with the Rails view context.

If you agree, I would be happy to contribute and create a pull request.

@yevhene

This comment has been minimized.

yevhene commented May 11, 2015

👍

@JonRowe

This comment has been minimized.

Member

JonRowe commented May 11, 2015

I'd support this, it's better than trying to "fix" the verification (because a view could be used by multiple controllers etc) and would mean the methods do actually exist on the view.

@yevhene

This comment has been minimized.

yevhene commented May 12, 2015

I have used:

helper.class.send(:attr_reader, :item)
assign(:item, value)

But assign_locals would be more convenient way.

assign_locals item: item
@codebeige

This comment has been minimized.

codebeige commented May 12, 2015

Wow, it has been quite some time since I originally posted this. 😎 I will have a look at it and create a PR whenever I find some time this week.

@yevhene

This comment has been minimized.

yevhene commented May 12, 2015

It is required for view and helper specs If mocks.verify_partial_doubles = true is used. I don't understand how people workaround this problem for a 6 months.

@JonRowe

This comment has been minimized.

Member

JonRowe commented May 13, 2015

Most people have probably either turned off verify_partial_doubles or worked around it by mixing in modules or the like. Also I'm not sure how common it is to write view specs.

@samphippen

This comment has been minimized.

Member

samphippen commented Apr 17, 2016

@codebeige

This comment has been minimized.

codebeige commented Apr 17, 2016

It is all just about clarity and convenience. In order to pass in the :locals option we would have to explicitly pass the partial in every render call as well.

Look at this gist for a basic implementation of my proposal that can easily be included in your project.

@samphippen

This comment has been minimized.

Member

samphippen commented Apr 17, 2016

@codebeige Can you explain to me why you prefer having add_local or whatever, and an implicit render call, instead of render :view_name, :locals => some_hash, it feels like the explicit call makes it more obvious what's going on.

That gist link doesn't look quite right (it's some html and JS), can you give us an updated one?

@codebeige

This comment has been minimized.

codebeige commented Apr 17, 2016

Fixed the link to the gist.

It is a matter of taste, I guess. I like to set the general prerequisites like the subject and mandatory assigns inside a before block for the context and then only override what is different for a specific example.

@samphippen

This comment has been minimized.

Member

samphippen commented Apr 17, 2016

@JonRowe I'd like your opinion here. So I'm not sure we should support this, given that :locals is a thing, and it's easy for users to put their own in. I'm not convinced this is a "core" RSpec thing, but I could be convinced. Do you have thoughts?

@JonRowe

This comment has been minimized.

Member

JonRowe commented Apr 18, 2016

@samphippen could we implement this with :locals? We're kind of about making these things easier but to be honest I'm not strongly committed to this, I don't think view specs are worth it anyway.

@samphippen

This comment has been minimized.

Member

samphippen commented Jul 5, 2016

Labelling this ticket as ready to go. Look at this gist for a starter implementation strategy: https://gist.github.com/codebeige/86905eb7dd7cef33230223ae456fe2f0

@samphippen

This comment has been minimized.

Member

samphippen commented Jul 5, 2016

Closing in favour of rspec/rspec-mocks#1102

@samphippen samphippen closed this Jul 5, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment