简体   繁体   English

使用 rspec 测试设计视图

[英]Testing devise views with rspec

I have generated Devise's views running rails g devise:views and would now like to test them.我已经生成了运行rails g devise:views Devise rails g devise:views ,现在想测试它们。

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' .对于 render 语句,它给了我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.在 render 语句之前 - 但它仍然给我同样的错误,我不确定如何使用它。

What am I doing wrong?我究竟做错了什么? Thanks for your help.谢谢你的帮助。

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 块中添加了这段代码:

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

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.这是必要的,因为 devise 在其控制器中使用了某些辅助方法。 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!将这些方法放在应用程序助手中,使它们可以在整个应用程序中访问,包括我的规范,并且确实通过了测试!

In rspec there's a configuration which doesn't allow you to stub out methods which are not defined.rspec 中,有一个配置不允许您删除未定义的方法。 With rails 4 it's even standard behavior:使用rails 4,它甚至是标准行为:

# 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.verify_partial_doubles更改为false或使用实际帮助程序。

Like Mike Fogg, I didn't like the idea of adding several methods to my ApplicationController just to get these view specs working.像 Mike Fogg 一样,我不喜欢将几个方法添加到我的 ApplicationController 只是为了让这些视图规范工作的想法。

I noticed that my rspec installation had a spec/mixins directory, so I did the following:我注意到我的 rspec 安装有一个spec/mixins目录,所以我做了以下事情:

# 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.现在我可以将这些方法混合到任何需要它们的规范中。

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 .我想做@MikeFogg 的解决方案——我在另一个视图规范中做了类似的事情来处理 Pundit——但当然我遇到了@ChrisEdwards 指出的关于需要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+.但是,我对在我的整个测试套件中关闭它没有太大兴趣,这是一个很好的检查,“通常推荐”并且默认为 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:我实际上首先在这里发布了一个解决方案,我在之前/之后的块中重新配置了 RSpec,但是我遇到了一个非常非常简单的方法来做到这一点,并且效果很好:

  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 .这自 2016 年以来一直存在,最初称为without_verifying_partial_doubles ,但被重命名为当前的without_partial_double_verification Does not appear to be documented anywhere I can see.在我能看到的任何地方似乎都没有记录。

Combining @sixty4bit answer with this answer here I came up with this solution:@sixty4bit 的答案这里的答案相结合,我想出了这个解决方案:

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

For RSpec 4, Rails 6 and Devise 4.7 I was unsuccessful with the other recommendations here or was wary of ramifications to my other testing or production code.对于 RSpec 4、Rails 6 和 Devise 4.7,我在这里的其他建议没有成功,或者担心对我的其他测试或生产代码的影响。

allow(view).to receive(:current_user).and_return(user)

in my view specs was what ultimately worked for me without any other configuration changes.在我看来,规格最终对我有用,没有任何其他配置更改。 (One has to define user first.) (必须先定义user 。)

It is not unlikely that there have been meaningful changes to the underlying gems/frameworks in recent years that invalidates some of the other solutions here.近年来,底层 gems/框架发生了有意义的变化,使这里的其他一些解决方案无效,这并非不可能。 It is even more likely that I have misconfigured Devise, RSpec and/or related helpers.更有可能是我错误地配置了 Devise、RSpec 和/或相关的帮助程序。

I confirmed that current_user was populated in rspec/rails/example/view_example_group.rb, but was returning nil during render .我确认current_user填充在 rspec/rails/example/view_example_group.rb 中,但在render期间返回nil I found this comment in view_example_group.rb and followed it:我在 view_example_group.rb 中找到了这条评论并关注了它:

        # The instance of `ActionView::Base` that is used to render the template.
        # Use this to stub methods _before_ calling `render`.
        #
        #     describe "widgets/new.html.erb" do
        #       it "shows all the widgets" do
        #         view.stub(:foo) { "foo" }
        #         render
        #         # ...
        #       end
        #     end
        def view
          _view
        end

https://github.com/rspec/rspec-rails/blob/main/lib/rspec/rails/example/view_example_group.rb https://github.com/rspec/rspec-rails/blob/main/lib/rspec/rails/example/view_example_group.rb

stub syntax is deprecated but view.stub(:current_user) { user } will also work (for now). stub语法已弃用,但view.stub(:current_user) { user }也可以使用(目前)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM