使用RSPEC+shoulda matchers测试控制器create方法save功能报错如何解决
问题根因及修复方案
1. 测试类型配置错误
你当前的测试声明为type: :routing,这类测试仅用于验证路由规则映射,不会正常执行控制器的业务逻辑,自然不会触发你写的create动作内的重定向逻辑,测试控制器需要改为type: :controller。
2. 测试断言写法错误
你写的expect(assigns[:restaurant].save).to redirect_to(gridurl_path)存在两个明显错误:
redirect_to匹配器是针对请求响应对象的,不能用来匹配save方法的布尔返回值- 你在断言中二次调用了
save方法,完全没有用到控制器内已经执行过的save逻辑结果,属于无效断言
3. 执行请求的时机错误
你把post :create的before块写在了context外部,此时context内定义的let(:params)还未生效,请求参数为空,直接导致控制器内的save失败,返回200渲染index页面。
4. 潜在的模型验证问题
即使测试逻辑修正后如果仍然报错,需要排查Restaurant模型的验证规则:
- 确认是否有除了
restaurantname、location_id之外的必填字段 - 确认
location_id对应的Location记录是否存在(如果配置了外键约束,关联记录不存在会导致save失败) - 确认参数键名
restaurantname和模型字段名完全一致,没有拼写错误
修正后的测试代码
# 注意修改测试类型为controller RSpec.describe HomeController, type: :controller do describe 'post #create' do context 'when the params are correct' do let(:params) { { restaurant: { restaurantname: "Buhari" ,location_id: 1} } } # before块移到context内部,确保params已经定义 before do post :create , params: params end it 'saves restaurant successfully and redirects to gridpage' do # 先验证记录成功创建 expect(Restaurant.exists?(restaurantname: "Buhari", location_id: 1)).to be true # 再验证响应是重定向到目标路径 expect(response).to redirect_to(gridurl_path) end end end end
额外验证步骤
如果修改后仍然报错,可以在控制器create动作的if @restaurant.save前加一行puts @restaurant.errors.full_messages,运行测试时看控制台输出的验证失败原因,针对性调整参数即可。
内容的提问来源于stack exchange,提问作者Robin
相关产品推荐
相关产品推荐

