问题
I have generated Devise's views running rails g devise:views
and would now like to test them.
This is what I have come up with:
require 'spec_helper'
describe "devise/sessions/new" do
before do
render
end
it "renders the form to log in" do
rendered.should have_selector("form", action: user_session_path, method: :post) do |form|
end
end
end
For the render statement it gives me undefined local variable or method 'resource'
. After googling around I found that I should add
@user.should_receive(:resource).and_return(User.new)
before the render statement - but it still gives me the same error, and I am not really sure how to use it.
What am I doing wrong? Thanks for your help.
回答1:
Just another thought incase anyone runs into the same issue. I wasn't a huge fan of adding code to my helpers just to make it so tests can pass so I ended up adding this code in a before block in my tests:
before do
view.stub(:resource).and_return(User.new)
view.stub(:resource_name).and_return(:user)
view.stub(:devise_mapping).and_return(Devise.mappings[:user])
end
回答2:
And whaddya know? I found this answer where someone had a similar problem. The solution is to include the following code in your application helper:
def resource_name
:user
end
def resource
@resource ||= User.new
end
def devise_mapping
@devise_mapping ||= Devise.mappings[:user]
end
This is necessary because devise is using certain helper methods in its controllers. If I access the views from my specs, however, these helper methods are not available, hence my tests fail. Putting these methods inside the application helper makes them accessible throughout the application, including my specs, and indeed the tests pass!
回答3:
In rspec there's a configuration which doesn't allow you to stub out methods which are not defined. With rails 4 it's even standard behavior:
# spec/spec_helper.rb
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
Because of that the highest-voted answer doesn't work with devise helpers. Change verify_partial_doubles
to false
or use an actual helper.
回答4:
Like Mike Fogg, I didn't like the idea of adding several methods to my ApplicationController just to get these view specs working.
I noticed that my rspec installation had a spec/mixins
directory, so I did the following:
# spec/mixins/devise_helpers.rb
module DeviseHelpers
def resource_name
:user
end
def resource
@resource ||= User.new
end
def devise_mapping
@devise_mapping ||= Devise.mappings[:user]
end
end
Then include in my spec:
# spec/views/devise/registrations/new.html.haml_spec.rb
require 'rails_helper'
include DeviseHelpers
describe 'devise/registrations/new.html.haml' do
it 'has a login link for existing users' do
render
expect(rendered).to have_link('Log in')
end
end
Now I can include mix those methods into any spec that needs them.
回答5:
I wanted to do @MikeFogg's solution - I do something similar in another view spec to deal with Pundit - but of course I ran up against the issue @ChrisEdwards pointed out with regards to needing mocks.verify_partial_doubles = false
.
However, I was not much interested in turning that off across my whole test suite, it's a good check, "generally recommended" and default in Rspec 4+.
I actually first posted a solution here where I reconfigured RSpec in before/after blocks, but I ran across a really, really easy way to do this, and it works great:
before(:each) do
without_partial_double_verification do
allow(view).to receive(:resource).and_return(Student.new)
allow(view).to receive(:resource_name).and_return(:student)
allow(view).to receive(:devise_mapping).and_return(Devise.mappings[:student])
end
# other before_each configuration here as needed
end
This has been around since 2016 and was originally called without_verifying_partial_doubles
, but was renamed to the current without_partial_double_verification
. Does not appear to be documented anywhere I can see.
回答6:
Combining @sixty4bit answer with this answer here I came up with this solution:
class DeviseHelpers < Module
def initialize(resource_name, resource_class)
@resource_name = resource_name
@resource_class = resource_class
end
def extended(base)
_resource_name = @resource_name
_resource_class = @resource_class
base.class_eval do
define_method :resource_name do
_resource_name
end
define_method :resource do
@resource ||= _resource_class.new
end
define_method :devise_mapping do
@devise_mapping ||= Devise.mappings[_resource_name]
end
end
end
end
This allows you to use it with different resources by requiring it at the top of your spec:
require 'support/devise_helpers'
and then calling it like this:
before do
view.extend(DeviseHelpers.new(:customer, Customer))
end
来源:https://stackoverflow.com/questions/14426746/testing-devise-views-with-rspec