我知道ActionView::Template::Error: undefined method [] for nil:NilClass
是一个常见错误,但其他页面似乎都没有回答我的问题。我有一个非常简单的测试登录,进入新的项目页面并通过填写表单来创建一个新项目。但是当我登录并加载rails时,Rspec / Capybara中的仪表板没有正确读取页面,并且在我们测试表单之前页面都失败了。
Rspec / Capybara似乎不知道什么是类项目并认为它是一个NilClass,尽管它在登录页面上的设计形式没有问题。
这是spec文件:
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'
这是仪表板控制器:
class DashboardController < ApplicationController
before_action :authenticate_user!
def index
@projects = Project.includes(:client).all
end
end
这是仪表板html(haml):
.container
.row
.col-sm-12
%p= link_to 'New Project', new_project_path, title: 'New Project'
%table
%thead
%tr
%th Client
%th Project
%th Urgency
%th Status
%th Due Date
%th Assigned To
%tbody
- @projects.each do |project|
%tr
%td= project.client.code
%td= link_to project.name, project
%td= project.urgency
%td= project.status
%td= project.due_date
%td= project.user.count
我设置了路线,所以如果您没有登录,则会看到登录页面,如果您已登录,则会看到仪表板:
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
答案 0 :(得分:1)
错误表明没有project
个对象。您是通过let!
来电创建的。正在收集let!
个变量以便在example
块中执行。以你自己的方式使用它们有点棘手,因为它们可能无法实例化,直到example
块开始,并且你明确before :each
示例使用此代码。但是,在这种情况下,您不必使用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
From the docs:
Use let to define a memoized helper method. The value will be cached
across multiple calls in the same example but not across examples.
答案 1 :(得分:1)
这与Capybara无法正确读取页面无关,而是表示您有一个Project对象,但没有与之关联的客户端。考虑到您的代码,最有可能的原因是在尝试创建Client对象时未通过模型验证。你应该切换到create!
,因为如果创建失败会引发问题,立即通知你一个问题,而create
只会返回一个未保存的对象。另外,在创建客户端和项目时,请检查test.log以获取有关验证失败的警告。
您的测试中还有其他一些问题需要修复,以避免进行不稳定的测试。
您的前一个块应该在结尾处有一个断言,以确保在下次访问之前登录已完成。像
这样的东西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')
切勿将eq
匹配器与current_path
一起使用。一旦使用带有current_path的eq移动到具有JS功能的驱动程序,将导致各种测试剥落。而是使用Capybara提供have_current_path
匹配器,它具有内置的等待/重试行为。所以不应该expect(current_path).to eq(new_project_path)
而应该做
expect(page).to have_current_path(new_project_path)