应用/控制器/ 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
应用/视图/产品/ new.html.erb:
<h1>New Product</h1>
<%= render 'form' %>
<%= link_to 'Back', products_path %>
应用/视图/产品/ _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
我们摆脱了错误,但失败仍然存在,stdout:
# 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
答案 0 :(得分:1)
当你在测试中调用post :create, product: ...
时,验证失败会导致渲染新视图。
new
视图会因调用options_for_select(@categories)
而导致错误,但未定义@categories
- 换句话说nil
。 options_for_select
期望参数为数组,并在.map
上调用nil
。
这不是唯一的问题 - 你也没有正确地嵌套输入。
<%= 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
答案 1 :(得分:0)
在堆栈跟踪中,当尝试保存@product
并且控制器想要呈现new
模板时,似乎验证失败。但是,您没有为此方案定义@categories
变量,这可能是map
nil
来自@categories
的调用。
因此,您可能应该执行以下两项操作:
create
数组添加到控制器中update
和<span>
操作的失败保存部分,以便失败保存方案正常运行