Rails 5.2.4/RSpec 3.12下如何正确校验模型外键?
解决Playground模型owner存在性校验失败的问题
问题根源
Rails 5及以上版本里,belongs_to关联默认会自动加上presence: true的校验,但如果你手动指定了class_name和foreign_key,有时候默认校验会出现不生效的情况;另外如果测试环境没跑迁移,数据库层面的null: false约束没生效,也会导致模型能被保存为nil。还有可能是shoulda-matchers的配置没到位,导致测试断言不生效。
修复方案
1. 显式添加模型校验
直接在app/models/playground.rb里手动加上owner的存在性校验,避免依赖默认行为:
class Playground < ApplicationRecord belongs_to :owner, class_name: "User", foreign_key: "owner_id" # 显式声明校验,确保生效 validates :owner, presence: true # 你的其他校验规则 validates :code, presence: true, uniqueness: true validates :name, presence: true end
2. 确保测试环境数据库迁移完成
运行命令同步测试环境的数据库结构:
rails db:test:prepare
数据库层面的null: false是最后一道防线,必须确保测试库有这个约束,避免模型绕过应用层校验存入非法数据。
3. 修正测试用例
用shoulda-matchers的正确写法
先确认spec/rails_helper.rb里已经启用了shoulda的模型匹配器:
RSpec.configure do |config| config.include Shoulda::Matchers::ActiveModel, type: :model config.include Shoulda::Matchers::ActiveRecord, type: :model end
然后在测试文件里写断言:
# spec/models/playground_spec.rb require 'rails_helper' RSpec.describe Playground, type: :model do # 先验证关联关系是否正确 it { is_expected.to belong_to(:owner).class_name('User') } # 再验证存在性校验 it { is_expected.to validate_presence_of(:owner) } end
手动写测试兜底
如果shoulda-matchers还是有问题,直接写原生测试逻辑:
it '不能没有owner' do # 只填code和name,不设置owner playground = Playground.new(code: 'demo-code', name: 'Demo Playground') expect(playground).to be_invalid # 检查错误提示是否正确 expect(playground.errors[:owner]).to include("can't be blank") end
4. 排查干扰因素
如果还是失败,打印错误信息看看具体原因:
it '排查校验问题' do playground = Playground.new(owner: nil, code: 'test', name: 'test') playground.valid? # 打印所有错误,看有没有其他问题 puts playground.errors.full_messages expect(playground).to be_invalid end
比如有没有回调方法自动给owner赋值,或者模型里有skip_validation的逻辑,这些都会导致校验失效。
内容的提问来源于stack exchange,提问作者user1185081
相关产品推荐
相关产品推荐

