Rspec Rails控制器测试无法传递图片参数问题排查
问题:Post控制器Create动作触发图片不能为空验证错误
我正在测试Post控制器的create动作,其余控制器的create动作均正常运行。经调试发现问题出在参数传递上:从Post工厂传递的图片在控制器中为空,导致创建Post时触发Images cannot be blank验证错误。
相关代码
post_spec.rb
describe 'POST #create' do it 'check post creation' do new_post = build :post, user: user attrs = new_post.attributes attrs.store(:images, [ Rack::Test::UploadedFile.new(image_url, 'image/jpg') ]) post posts_path, params: { post: new_post.attributes } expect(response).to be_success end end
post.rb(模型)
class Post < ApplicationRecord belongs_to :user has_many :comments, dependent: :destroy has_many :likes, dependent: :destroy has_many_attached :images validates :user_id, presence: true validates :title, presence: true, length: { minimum: 1, maximum: 1000 } validates :images, presence: true, limit: { min: 1, max: 10 }, content_type: ['image/jpg', 'image/jpeg', 'image/png', 'image/webp'] end
post.rb(FactoryBot工厂)
image_url = '/home/dev/Downloads/a1.jpg' FactoryBot.define do factory :post do title { Faker::Lorem.sentence } images { Rack::Test::UploadedFile.new(image_url, 'image/jpg') } user end end
post_controller.rb
def create @post = Post.new(post_params) if @post.save redirect_to post_url(@post), notice: 'Post was successfully created.' else render :new, status: :unprocessable_entity end end def post_params params.require(:post).permit(:title, :user_id, images: []) end
调试信息
- 检查
@post错误输出:['Images cannot be blank'] - post_spec中的
new_post实例:#<Post id: nil, title: "Sequi excepturi quam quos.", user_id: 307, created_at: nil, updated_at: nil> - post_spec中的
new_post.attributes内容:{"id"=>nil, "title"=>"Sequi excepturi quam quos.", "user_id"=>307, "created_at"=>nil, "updated_at"=>nil}
问题分析
new_post.attributes仅包含模型的数据库字段,不会包含Active Storage的附件(images),因为附件不属于模型的核心数据库属性。- 你在代码中给
attrs添加了images参数,但最终发起请求时用的是new_post.attributes,导致添加的图片参数根本没传到控制器。 - 工厂中定义的
images是单个Rack::Test::UploadedFile实例,但模型用的是has_many_attached,需要传入数组格式的图片参数。
解决办法
方法一:修正spec中的参数传递
直接使用添加了图片的attrs发起请求,同时移除attributes里不需要的字段(如id、created_at):
describe 'POST #create' do it 'check post creation' do new_post = build :post, user: user attrs = new_post.attributes.except('id', 'created_at', 'updated_at') attrs[:images] = [ Rack::Test::UploadedFile.new('/home/dev/Downloads/a1.jpg', 'image/jpg') ] post posts_path, params: { post: attrs } expect(response).to be_success end end
方法二:使用attributes_for构建参数(更简洁)
直接用FactoryBot的attributes_for方法生成参数,再合并图片:
describe 'POST #create' do it 'check post creation' do post_attrs = attributes_for(:post).merge( images: [ Rack::Test::UploadedFile.new('/home/dev/Downloads/a1.jpg', 'image/jpg') ] ) post posts_path, params: { post: post_attrs } expect(response).to be_success end end
方法三:修改工厂适配多附件格式
把工厂中的images改成数组格式,这样使用attributes_for时无需额外处理:
image_url = '/home/dev/Downloads/a1.jpg' FactoryBot.define do factory :post do title { Faker::Lorem.sentence } images { [Rack::Test::UploadedFile.new(image_url, 'image/jpg')] } user end end
对应的spec可以简化为:
describe 'POST #create' do it 'check post creation' do post posts_path, params: { post: attributes_for(:post) } expect(response).to be_success end end
内容的提问来源于stack exchange,提问作者Usman Ahmed
相关产品推荐
相关产品推荐

