Testing devise views with rspec

◇◆丶佛笑我妖孽 提交于 2019-12-22 03:47:22

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!