Rspec&;Capybara:ActionView::Template::Error:未定义的方法`';零级:零级

Rspec&;Capybara:ActionView::Template::Error:未定义的方法`';零级:零级,rspec,capybara,ruby-on-rails-5,Rspec,Capybara,Ruby On Rails 5,我知道nil:NilClass的ActionView::Template::Error:undefined方法[]是一个常见错误,但其他页面似乎都没有回答我的问题。我有一个非常简单的测试,登录,进入新项目页面,通过填写表单创建一个新项目。但是,当我登录并rails加载Rspec/Capybara中的仪表板时,该仪表板无法正确读取页面,并且在我们测试表单之前页面就失败了 Rspec/Capybara似乎不知道什么类项目,并且认为它是一个NilClass,尽管它对登录页面上的designe表单没有问

我知道nil:NilClass的
ActionView::Template::Error:undefined方法[]是一个常见错误,但其他页面似乎都没有回答我的问题。我有一个非常简单的测试,登录,进入新项目页面,通过填写表单创建一个新项目。但是,当我登录并rails加载Rspec/Capybara中的仪表板时,该仪表板无法正确读取页面,并且在我们测试表单之前页面就失败了

Rspec/Capybara似乎不知道什么类项目,并且认为它是一个NilClass,尽管它对登录页面上的designe表单没有问题

以下是规范文件:

require 'rails_helper'

describe "Creating a new project" do

  let!(:client) { Client.create(name: "abc", code: "abc") }
  let!(:project) { Project.create(name: "abc", client: client) }
  let!(:user) { User.create(email: "derp@example.com", password: "123123123") }

  # Sign In with user
  before :each do
    visit root_url
    fill_in 'user[email]', with: 'derp@example.com'
    fill_in 'user[password]', with: '123123123'
    find('input[name="commit"]').click
  end

  it "saves new project and returns to list of projects on root" do
    visit root_url
    click_on 'New Project'
    expect(current_path).to eq(new_project_path)

    fill_in "Name", with: "My New Cool Project"
    fill_in "Details", with: "Praise the sun!"
    select "urgency_fire", :from => "Urgency"
    select "status_in_progress", :from => "Status"
    select "ABC - abc", :from => "Client"
    fill_in "Due date", with: Time.now.to_s
    find('input[name="commit"]').click

    expect(current_path).to eq(root_path)
    expect(page).to have_text("My New Cool Project")

  end
end
以下是测试输出:

1) Creating a new project saves new project and returns to list of projects on root
 Failure/Error: %td= project.client.code

 ActionView::Template::Error:
   undefined method `code' for nil:NilClass
 # ./app/views/dashboard/index.html.haml:17:in `block in _app_views_dashboard_index_html_haml__3165971454273894605_70112485977840'
 # ./app/views/dashboard/index.html.haml:15:in `_app_views_dashboard_index_html_haml__3165971454273894605_70112485977840'
 # ./spec/features/new_project_form_spec.rb:14:in `block (2 levels) in <top (required)>'
 # ------------------
 # --- Caused by: ---
 # NoMethodError:
 #   undefined method `code' for nil:NilClass
 #   ./app/views/dashboard/index.html.haml:17:in `block in _app_views_dashboard_index_html_haml__3165971454273894605_70112485977840'
我已经设置了路线,因此如果您未登录,您将看到登录页面,如果您登录,您将看到仪表板:

Rails.application.routes.draw do

    # devise gem routes
    devise_for :users

    # Dashboard
    authenticated :user do
        root :to => 'dashboard#index'
    end

    # Login
    devise_scope :user do
        root to: "devise/sessions#new"
    end

    # Projects
    resources :projects

    # Send all unknown pages to root
    if ENV["RAILS_ENV"] == "production"
        get '*path' => redirect('/')
    end

end

错误表明没有
项目
对象。你正在用你的
let创建它呼叫。
let变量,以便在
示例
块内执行。按照您的方式使用它们是有点棘手的,因为在
示例
块启动之前,它们可能不会被实例化,并且您在前面显式地
:每个
示例都使用此代码。但是,在这种情况下,您不必使用
let
。由于测试中未引用值,因此可以尝试以下操作:

# Given an existing project and logged-in user
before :each do
  project = Project.create(name: 'abc',
    client: Client.create(name: "abc", code: "abc"),
    code: 'abc')
  user = User.create(email: "derp@example.com", password: "123123123")

  visit root_url
  fill_in 'user[email]', with: 'derp@example.com'
  fill_in 'user[password]', with: '123123123'
  find('input[name="commit"]').click
end
使用let定义一个记忆化的帮助器方法。该值将被缓存

跨同一示例中的多个调用,但不跨示例。

这与Capybara没有正确读取页面无关,而是表示您有一个项目对象,但没有与之关联的客户端。考虑到您的代码,最可能的原因是在尝试创建客户机对象时模型验证失败。您应该切换到
create因为如果创建失败,将引发此问题,并立即通知您问题,而
create
将只返回一个未保存的对象。另外,在创建客户机和项目时,请检查test.log以获取有关验证失败的警告

您的测试中还存在一些其他问题,您应该解决这些问题,以避免将来出现不稳定的测试

  • before块的末尾应该有一个断言,以确保在调用下一次访问之前登录已完成。差不多

    expect(page).to have_content('You are now logged in!')
    

  • 切勿将
    eq
    匹配器与
    当前路径一起使用。一旦你移动到一个JS驱动程序,使用eq和当前路径将导致各种各样的测试失败。相反,请使用内置了等待/重试行为的Capybara Provider
    have_current_path
    匹配器。因此,与其使用
    expect(当前路径),不如使用eq(新项目路径)

    expect(page).to have_current_path(new_project_path)
    

  • 谢谢Greg,这已经解决了!谢谢你的测试帮助,托马斯!我刚开始写测试,所以我很感激你的反馈!
    expect(page).to have_content('You are now logged in!')
    
    expect(page).to have_current_path('whatever path the user should be directed to on successful login')
    
    expect(page).to have_current_path(new_project_path)