为什么之前:保存回调挂钩不从FactoryGirl.create()调用?

fea*_*ool 6 ruby datamapper factory-bot

这个简单的例子使用DataMapper的before :save回调(aka hook)来递增callback_count.callback_count初始化为0,并应由回调设置为1.

通过以下方式创建TestObject时,将调用此回调:

TestObject.create()
Run Code Online (Sandbox Code Playgroud)

但是当FactoryGirl通过以下方式创建时会跳过回调:

FactoryGirl.create(:test_object)
Run Code Online (Sandbox Code Playgroud)

知道为什么吗?[注意:我正在运行ruby 1.9.3,factory_girl 4.2.0,data_mapper 1.2.0]

详细信息如下......

DataMapper模型

# file: models/test_model.rb
class TestModel
  include DataMapper::Resource

  property :id, Serial
  property :callback_count, Integer, :default => 0

  before :save do
    self.callback_count += 1
  end
end
Run Code Online (Sandbox Code Playgroud)

FactoryGirl声明

# file: spec/factories.rb
FactoryGirl.define do
  factory :test_model do
  end
end
Run Code Online (Sandbox Code Playgroud)

RSpec测试

# file: spec/models/test_model_spec.rb
require 'spec_helper'

describe "TestModel Model" do
  it 'calls before :save using TestModel.create' do
    test_model = TestModel.create
    test_model.callback_count.should == 1
  end
  it 'fails to call before :save using FactoryGirl.create' do
    test_model = FactoryGirl.create(:test_model)
    test_model.callback_count.should == 1
  end
end
Run Code Online (Sandbox Code Playgroud)

测试结果

Failures:

  1) TestModel Model fails to call before :save using FactoryGirl.create
     Failure/Error: test_model.callback_count.should == 1
       expected: 1
            got: 0 (using ==)
     # ./spec/models/test_model_spec.rb:10:in `block (2 levels) in <top (required)>'

Finished in 0.00534 seconds
2 examples, 1 failure
Run Code Online (Sandbox Code Playgroud)

fea*_*ool 1

解决了。

@Jim Stewart 向我指出了这个 FactoryGirl 问题,其中写着“FactoryGirl 在[它创建的]实例上调用 save!”。在 DataMapper 的世界中,save!明确不运行回调——这解释了我所看到的行为。(但它并没有解释为什么它适用于@enthrops!)

该链接还提供了一些专门针对 DataMapper 的解决方法,我可能会选择其中之一。不过,如果未修改的 FactoryGirl 能够与 DataMapper 配合良好,那就太好了。

更新

这是由thoughtbot 的Joshua Clayton 建议的代码。我将其添加到我的spec/factories.rb文件中,test_model_spec.rb现在顺利通过。凉豆子。

# file: factories.rb
class CreateForDataMapper
  def initialize
    @default_strategy = FactoryGirl::Strategy::Create.new
  end

  delegate :association, to: :@default_strategy

  def result(evaluation)
    evaluation.singleton_class.send :define_method, :create do |instance|
      instance.save ||
        raise(instance.errors.send(:errors).map{|attr,errors| "- #{attr}: #{errors}"    }.join("\n"))
    end

    @default_strategy.result(evaluation)
  end
end

FactoryGirl.register_strategy(:create, CreateForDataMapper)
Run Code Online (Sandbox Code Playgroud)

更新2

出色地。也许我说得太早了。添加 CreateForDataMapper 可以修复一项特定测试,但似乎会破坏其他测试。所以我暂时不回答我的问题。还有人有好的解决办法吗?