未定义的方法“映射”错误应该创建模型测试
undefined method `map' error on should create model test
app/controllers/products_controller.rb:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /products
# GET /products.json
def index
#@products = Product.all
@products = Product.all
if params[:search]
@products = Product.search(params[:search]).order("created_at DESC").paginate(page: params[:page], per_page: 5)
else
@products = Product.all.order('created_at DESC').paginate(page: params[:page], per_page: 5)
end
if session[:cart] then
@cart = session[:cart]
else
@cart = {}
end
end
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
if current_user.admin?
#@product = Product.new
@product = Product.new
@categories = Category.all.map{|c| [ c.name, c.id ] }
end
end
# GET /products/1/edit
def edit
if current_user.admin?
@categories = Category.all.map{|c| [ c.name, c.id ] }
end
end
# POST /products
# POST /products.json
def create
if current_user.admin?
@product = Product.new(product_params)
@product.category_id = params[:category_id]
respond_to do |format|
if @product.save
format.html { redirect_to @product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: @product }
else
format.html { render :new }
format.json { render json: @product.errors, status: :unprocessable_entity }
end
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
if current_user.admin?
@product.category_id = params[:category_id]
respond_to do |format|
if @product.update(product_params)
format.html { redirect_to @product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: @product }
else
format.html { render :edit }
format.json { render json: @product.errors, status: :unprocessable_entity }
end
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
if current_user.admin?
@product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
@product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:title, :description, :photo, :price, :category, :subcategory)
end
end
app/views/products/new.html.erb:
<h1>New Product</h1>
<%= render 'form' %>
<%= link_to 'Back', products_path %>
app/views/products/_form.html.erb:
<%= form_for(@product, multipart: true) do |f| %>
<% if @product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% @product.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :image %><br>
<%= f.file_field :photo %>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.number_field :price, :step => "0.01" %>
</div>
<div class="field">
<%= f.label :category %><br>
<%= select_tag(:category_id, options_for_select(@categories), :prompt => "Select one!") %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
我的测试在 test/controllers/products_controller_test.rb:
test "should create product" do
sign_in users(:admin)
assert_difference('Product.count') do
post :create, product: { category_id: @product.category, description: @product.description, photo_content_type: @product.photo_content_type, photo_file_name: @product.photo_file_name, photo_file_size: @product.photo_file_size, photo_updated_at: @product.photo_updated_at, price: @product.price, title: @product.title }
end
assert_redirected_to product_path(assigns(:product))
end
Causing this error:
Finished in 0.816541s, 24.4936 runs/s, 35.5157 assertions/s.
1) Error:
ProductsControllerTest#test_should_create_product:
ActionView::Template::Error: undefined method `map' for nil:NilClass
app/views/products/_form.html.erb:32:in `block in _app_views_products__form_html_erb__4489093195482743578_70189257075560'
app/views/products/_form.html.erb:1:in `_app_views_products__form_html_erb__4489093195482743578_70189257075560'
app/views/products/new.html.erb:3:in `_app_views_products_new_html_erb__3438187527367239596_70189257283820'
app/controllers/products_controller.rb:57:in `block (2 levels) in create'
app/controllers/products_controller.rb:52:in `create'
test/controllers/products_controller_test.rb:29:in `block (2 levels) in <class:ProductsControllerTest>'
test/controllers/products_controller_test.rb:28:in `block in <class:ProductsControllerTest>'
20 runs, 29 assertions, 0 failures, 1 errors, 0 skips
我 运行 我的测试 rake test:functionals
。其他测试都没问题,只有这个测试有问题
app/views/products/_form.html.erb:中甚至没有任何名为map
的方法。所以我不知道这个错误的原因。
如果您想查看整个项目:https://github.com/mertyildiran/SCOR
变化:
根据回答:
我在表格中进行了此更改:<%= f.collection_select(:category_id, Category.all, :id, :name) %>
在产品控制器中:params.require(:product).permit(:title, :description, :photo, :price, :category_id)
以及新版测试:
test "should create product" do
sign_in users(:admin)
image = fixture_file_upload "../../public/assets/products/1/original/" + @product.photo_file_name
puts image.inspect
assert_difference('Product.count') do
post :create, product: { category_id: @product.category, description: @product.description, photo: image, price: @product.price, title: @product.title }
end
assert_redirected_to product_path(assigns(:product))
end
我们摆脱了错误,但失败仍然存在,标准输出:
# Running:
.......#<Rack::Test::UploadedFile:0x007f5a1894fdf8 @content_type=nil, @original_filename="ra_unisex_tshirt_x1000_fafafa-ca443f4786_front-c_235_200_225_294-bg_f8f8f8_(7).jpg", @tempfile=#<Tempfile:/tmp/ra_unisex_tshirt_x1000_fafafa-ca443f4786_front-c_235_200_225_294-bg_f8f8f8_(7).jpg20160515-31919-1j7oypa>>
F............
Finished in 0.800235s, 24.9926 runs/s, 37.4890 assertions/s.
1) Failure:
ProductsControllerTest#test_should_create_product [/home/mertyildiran/Documents/SCOR/test/controllers/products_controller_test.rb:25]:
"Product.count" didn't change by 1.
Expected: 3
Actual: 2
20 runs, 30 assertions, 1 failures, 0 errors, 0 skips
验证失败原因:
<div id="error_explanation">
<h2>2 errors prohibited this product from being saved:</h2>
<ul>
<li>Photo content type is invalid</li>
<li>Photo is invalid</li>
</ul>
</div>
已修复 image.content_type = @product.photo_content_type
从堆栈跟踪来看,似乎在尝试保存 @product
时验证失败,并且控制器想要呈现 new
模板。但是您没有为这种情况定义 @categories
变量,这可能是对 nil
的 map
调用的来源。
因此,您可能应该执行以下两项操作:
- 将
@categories
数组添加到控制器中 create
和 update
操作的失败保存部分,以便失败保存场景正常工作
- 修复测试代码,以便在创建产品时验证不会失败。
当您在测试中调用 post :create, product: ...
时,验证失败导致呈现新视图。
new
视图然后通过调用 options_for_select(@categories)
导致错误,但 @categories
未定义 - 换句话说 nil
。 options_for_select
期望参数是一个数组并在 nil
.
上调用 .map
这不是唯一的问题 - 您也没有正确嵌套输入。
<%= select_tag(:category_id, options_for_select(@categories), :prompt => "Select one!") %>
最终会出现在 params[:category_id]
而不是 params[:product][:category_id]
。
您要做的是使用 collection_select
并将其绑定到模型:
<div class="field">
<%= f.label :category_id %><br>
<%= f.collection_select(:category_id, Category.all, :id, :name) %>
</div>
您还应确保将正确的参数列入白名单 - 如果您很难跟踪它们,请确保也对其进行测试!
def product_params
params.require(:product).permit(:title, :description, :photo, :price, :category_id, :subcategory)
end
如果您想准确调试导致验证失败的属性,有几个选项:
assigns(:product)
将为您提供控制器中的 @product
实例变量。 assert_equals(@product.errors.full_messages, [])
给你一个列表。您必须先调用控制器操作!
- 您可以执行
assert_equals(@response.body, '')
来获取页面 HTML 的转储。您必须先调用控制器操作!
- 使用
$ tail -f logs/test.log
在日志上保留一个标签
app/controllers/products_controller.rb:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /products
# GET /products.json
def index
#@products = Product.all
@products = Product.all
if params[:search]
@products = Product.search(params[:search]).order("created_at DESC").paginate(page: params[:page], per_page: 5)
else
@products = Product.all.order('created_at DESC').paginate(page: params[:page], per_page: 5)
end
if session[:cart] then
@cart = session[:cart]
else
@cart = {}
end
end
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
if current_user.admin?
#@product = Product.new
@product = Product.new
@categories = Category.all.map{|c| [ c.name, c.id ] }
end
end
# GET /products/1/edit
def edit
if current_user.admin?
@categories = Category.all.map{|c| [ c.name, c.id ] }
end
end
# POST /products
# POST /products.json
def create
if current_user.admin?
@product = Product.new(product_params)
@product.category_id = params[:category_id]
respond_to do |format|
if @product.save
format.html { redirect_to @product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: @product }
else
format.html { render :new }
format.json { render json: @product.errors, status: :unprocessable_entity }
end
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
if current_user.admin?
@product.category_id = params[:category_id]
respond_to do |format|
if @product.update(product_params)
format.html { redirect_to @product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: @product }
else
format.html { render :edit }
format.json { render json: @product.errors, status: :unprocessable_entity }
end
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
if current_user.admin?
@product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
@product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:title, :description, :photo, :price, :category, :subcategory)
end
end
app/views/products/new.html.erb:
<h1>New Product</h1>
<%= render 'form' %>
<%= link_to 'Back', products_path %>
app/views/products/_form.html.erb:
<%= form_for(@product, multipart: true) do |f| %>
<% if @product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% @product.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :image %><br>
<%= f.file_field :photo %>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.number_field :price, :step => "0.01" %>
</div>
<div class="field">
<%= f.label :category %><br>
<%= select_tag(:category_id, options_for_select(@categories), :prompt => "Select one!") %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
我的测试在 test/controllers/products_controller_test.rb:
test "should create product" do
sign_in users(:admin)
assert_difference('Product.count') do
post :create, product: { category_id: @product.category, description: @product.description, photo_content_type: @product.photo_content_type, photo_file_name: @product.photo_file_name, photo_file_size: @product.photo_file_size, photo_updated_at: @product.photo_updated_at, price: @product.price, title: @product.title }
end
assert_redirected_to product_path(assigns(:product))
end
Causing this error:
Finished in 0.816541s, 24.4936 runs/s, 35.5157 assertions/s.
1) Error:
ProductsControllerTest#test_should_create_product:
ActionView::Template::Error: undefined method `map' for nil:NilClass
app/views/products/_form.html.erb:32:in `block in _app_views_products__form_html_erb__4489093195482743578_70189257075560'
app/views/products/_form.html.erb:1:in `_app_views_products__form_html_erb__4489093195482743578_70189257075560'
app/views/products/new.html.erb:3:in `_app_views_products_new_html_erb__3438187527367239596_70189257283820'
app/controllers/products_controller.rb:57:in `block (2 levels) in create'
app/controllers/products_controller.rb:52:in `create'
test/controllers/products_controller_test.rb:29:in `block (2 levels) in <class:ProductsControllerTest>'
test/controllers/products_controller_test.rb:28:in `block in <class:ProductsControllerTest>'
20 runs, 29 assertions, 0 failures, 1 errors, 0 skips
我 运行 我的测试 rake test:functionals
。其他测试都没问题,只有这个测试有问题
app/views/products/_form.html.erb:中甚至没有任何名为map
的方法。所以我不知道这个错误的原因。
如果您想查看整个项目:https://github.com/mertyildiran/SCOR
变化:
根据回答:
我在表格中进行了此更改:<%= f.collection_select(:category_id, Category.all, :id, :name) %>
在产品控制器中:params.require(:product).permit(:title, :description, :photo, :price, :category_id)
以及新版测试:
test "should create product" do
sign_in users(:admin)
image = fixture_file_upload "../../public/assets/products/1/original/" + @product.photo_file_name
puts image.inspect
assert_difference('Product.count') do
post :create, product: { category_id: @product.category, description: @product.description, photo: image, price: @product.price, title: @product.title }
end
assert_redirected_to product_path(assigns(:product))
end
我们摆脱了错误,但失败仍然存在,标准输出:
# Running:
.......#<Rack::Test::UploadedFile:0x007f5a1894fdf8 @content_type=nil, @original_filename="ra_unisex_tshirt_x1000_fafafa-ca443f4786_front-c_235_200_225_294-bg_f8f8f8_(7).jpg", @tempfile=#<Tempfile:/tmp/ra_unisex_tshirt_x1000_fafafa-ca443f4786_front-c_235_200_225_294-bg_f8f8f8_(7).jpg20160515-31919-1j7oypa>>
F............
Finished in 0.800235s, 24.9926 runs/s, 37.4890 assertions/s.
1) Failure:
ProductsControllerTest#test_should_create_product [/home/mertyildiran/Documents/SCOR/test/controllers/products_controller_test.rb:25]:
"Product.count" didn't change by 1.
Expected: 3
Actual: 2
20 runs, 30 assertions, 1 failures, 0 errors, 0 skips
验证失败原因:
<div id="error_explanation">
<h2>2 errors prohibited this product from being saved:</h2>
<ul>
<li>Photo content type is invalid</li>
<li>Photo is invalid</li>
</ul>
</div>
已修复 image.content_type = @product.photo_content_type
从堆栈跟踪来看,似乎在尝试保存 @product
时验证失败,并且控制器想要呈现 new
模板。但是您没有为这种情况定义 @categories
变量,这可能是对 nil
的 map
调用的来源。
因此,您可能应该执行以下两项操作:
- 将
@categories
数组添加到控制器中create
和update
操作的失败保存部分,以便失败保存场景正常工作 - 修复测试代码,以便在创建产品时验证不会失败。
当您在测试中调用 post :create, product: ...
时,验证失败导致呈现新视图。
new
视图然后通过调用 options_for_select(@categories)
导致错误,但 @categories
未定义 - 换句话说 nil
。 options_for_select
期望参数是一个数组并在 nil
.
.map
这不是唯一的问题 - 您也没有正确嵌套输入。
<%= select_tag(:category_id, options_for_select(@categories), :prompt => "Select one!") %>
最终会出现在 params[:category_id]
而不是 params[:product][:category_id]
。
您要做的是使用 collection_select
并将其绑定到模型:
<div class="field">
<%= f.label :category_id %><br>
<%= f.collection_select(:category_id, Category.all, :id, :name) %>
</div>
您还应确保将正确的参数列入白名单 - 如果您很难跟踪它们,请确保也对其进行测试!
def product_params
params.require(:product).permit(:title, :description, :photo, :price, :category_id, :subcategory)
end
如果您想准确调试导致验证失败的属性,有几个选项:
assigns(:product)
将为您提供控制器中的@product
实例变量。assert_equals(@product.errors.full_messages, [])
给你一个列表。您必须先调用控制器操作!- 您可以执行
assert_equals(@response.body, '')
来获取页面 HTML 的转储。您必须先调用控制器操作! - 使用
$ tail -f logs/test.log
在日志上保留一个标签