从RSpec示例调用时,不会触发ActiveRecord before_validation回调

时间:2013-01-14 23:26:01

标签: ruby-on-rails ruby rspec factory-bot

我已经阅读了大部分有关类似问题的答案,但尚未找到解决方案。代码如下:

设置

class Person < ActiveRecord::Base
  # Other inconsequential code
  # ...
  has_and_belongs_to_many :roles
  before_validation: attach_roles
  # ...
  def attach_roles
    roles << Role.default if roles.blank?
  end
end

class Role < ActiveRecord::Base

  has_and_belongs_to_many: people

  def self.default
  #
  # Get default role
  #
  end

end

测试

require 'spec_helper'

RSpec.configure do |config|
  config.include FactoryGirl::Syntax::Methods
end

describe Person do

  context "constructor" do

    it "creates a valid Person" do
      person = build(:person)
      person.should_receive(:attach_roles) # This works
      person.valid?
      puts person.roles.inspect # Returns []
    end    

    it "creates a another valid Person" do
      person = build(:person)
      person.valid?
      person.should be_valid # This fails
      puts person.roles.inspect # Returns []
    end

  end


end

问题

似乎没有调用attach_roles回调。但是should_receive断言是真的

在控制台中

p = FactoryGirl.build(:person)
p.roles # []
p.valid? # true
p.roles # [<Role>]

有人能够解释一下吗?

附注:我们也欢迎尝试创建默认角色的任何其他想法。

的Env

  • rails 3.2.1
  • ruby​​ 1.9.3
  • rspec 2.12.0
  • factory_girl 4.1.0

1 个答案:

答案 0 :(得分:1)

您的should_receive测试证明attach_roles正在被调用,它只是没有按照您的预期进行。

我认为有两件事让我感到担忧。

一个与@apneadiving指出的相同。

尝试在Ruby中分配实例变量时,必须使用self.roles。我不确定<< x的工作原理。如果它是roles= roles + x之类的语法糖,那么你需要self.roles,但如果它是roles.insert(x)那么你就不需要self.roles。如果有疑问,<<将始终按照您的期望行事。

我担心的另一件事是你在一个尚未持久化的模型上使用Role。该操作具有破坏性,并将尝试持久化def attach_roles roles.build(Role.default) end 。由于您在第一次创建模型时可能会调用该函数,因此该代码仅在未持久化时才会运行。虽然我觉得它主要起作用,但我不确定这是不是你想要的。我认为你会更好:

Role.default

这假设{{1}}正在返回属性哈希。我对你的意图可能是错的。

我希望有所帮助。