为关联添加模型常量(未定义的方法“relation_delegate_class”)

Stubing a model constant for assosiation (undefined method `relation_delegate_class')

我有一个与 Permission 模型关联的 Lesson 模型:

app/models/lesson.rb:

class Lesson < ActiveRecord::Base
  has_many :permissions, :class_name => 'Permission', as: :permissible, dependent: :destroy
  ...

  def create_permissions
    Permission::DEFAULTS[:lesson].each do |action, value|
      ..
    end
  end

app/models/permission.rb:

class Permission < ActiveRecord::Base
  DEFAULTS = {
    lesson: {some_more_action: 15}
  }

  belongs_to :permissible, polymorphic: true
end

我用了RSpecstub_const方法stub a nested defined constant:

spec/models/lesson_spec.rb:

require 'rails_helper'

RSpec.describe Lesson, :type => :model do
  describe "#create_permissions" do
    let!(:lesson) { FactoryGirl.build_stubbed :lesson }

    before(:each) do
      stub_const('Permission::DEFAULTS', {lesson: {some_action: 5}})
    end

    it 'should create permissions' do
      lesson.create_permissions

      permission = lesson.permissions.first

      ...
    end
  end
end

但规范因错误而失败:

Failure/Error: permission = lesson.permissions.first
NoMethodError:
  undefined method `relation_delegate_class' for Permission:Module
 # /home/install/.rvm/gems/ruby-2.1.4/gems/activerecord-4.2.3/lib/active_record/relation/delegation.rb:112:in `relation_class_for'
 # /home/install/.rvm/gems/ruby-2.1.4/gems/activerecord-4.2.3/lib/active_record/relation/delegation.rb:106:in `create'
 # /home/install/.rvm/gems/ruby-2.1.4/gems/activerecord-4.2.3/lib/active_record/associations/collection_association.rb:41:in `reader'
 # /home/install/.rvm/gems/ruby-2.1.4/gems/activerecord-4.2.3/lib/active_record/associations/builder/association.rb:115:in `permissions'
 # ./spec/models/lesson_spec.rb:285:in `block (3 levels) in <top (required)>'

看起来 permissions 不再 rails 与课程模型相关联。关于如何获得这一轮的任何想法。

Gem 版本:rspec-3.3.0rspec-rails-3.3.3rails-4.2.3.

我想到的唯一选择就是在示例中明确设置常量:

before(:each) do
  Permission::DEFAULTS = {lesson: {some_action: 5}}
end

但我觉得这不是个好主意。它还会发出一些警告:

spec/models/lesson_spec.rb:279: warning: already initialized constant Permission::DEFAULTS
app/models/permission.rb:2: warning: previous definition of DEFAULTS was here

仔细查看此错误消息:

NoMethodError:
  undefined method `relation_delegate_class' for Permission:Module

Permission 在这里被描述为 Module,而不是 Class。我认为发生这种情况是因为在存根嵌套常量的那一刻,Permission class 尚未加载,并且 RSpec 也必须通过 Module.[=20 存根它=]

试试这个作为解决方法:

before(:each) do
  Permission # load real ActiveRecord Permission class
  stub_const('Permission::DEFAULTS', {lesson: {some_action: 5}})
end

编辑:附带说明一下,我不认为将嵌套常量暴露给其他 classes 是个好主意,您几乎无法控制常量(只是更改名称或值)并且您不能像使用方法那样包装任何行为。我建议将 public Permission 的 API 更改为 using 方法,它也会更容易存根:

class Permission < ActiveRecord::Base
  DEFAULTS = {
    lesson: {some_more_action: 15}
  }

  def self.defaults
    DEFAULTS
  end    
end

在您的规范中:

before(:each) do
  allow(Permission).to receive(:defaults).and_return(lesson: {some_action: 5})
end