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

时间:2013-03-03 00:10:13

标签: ruby factory-bot datamapper

这个简单的例子使用DataMapper的before :save回调(又名钩子)来递增callback_count。 callback_count初始化为0,应该通过回调设置为1.

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

TestObject.create()

但是FactoryGirl通过以下方式创建了回调:

FactoryGirl.create(:test_object)

知道为什么吗? [注意:我正在运行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

FactoryGirl声明

# file: spec/factories.rb
FactoryGirl.define do
  factory :test_model do
  end
end

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

测试结果

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

3 个答案:

答案 0 :(得分:3)

至少对于factory_girl 4.2(不知道从哪个版本支持它),通过使用custom methods to persist objects还有另一个工作变暖。正如a response to an issue about it in Github中所述,只需要调用save而不是save!

FactoryGirl.define do
  to_create do |instance|
    if !instance.save
      raise "Save failed for #{instance.class}"
    end
  end
end

当然它并不理想,因为它应该在FactoryGirl核心中起作用,但我认为现在它是最好的解决方案,而且目前我没有与其他测试发生冲突......

需要注意的是,你必须在每个工厂定义它(但对我来说这不是一个不方便)

答案 1 :(得分:1)

解决。

@Jim Stewart把我指向this FactoryGirl issue,它说“FactoryGirl在它创建的实例上调用save!”。在DataMapper的世界中,save!明确表示运行回调 - 这解释了我所看到的行为。 (但它没有解释为什么它适用于@enthrops!)

同一个链接提供了一些专门针对DataMapper的解决方法,我可能会选择其中一个。如果未修改的FactoryGirl与DataMapper一起玩得很好,那就太好了。

更新

以下是思想机器人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)

更新2

好。也许我说得太早了。添加CreateForDataMapper修复了一个特定的测试,但似乎打破了其他测试。所以我现在没有回答我的问题。其他人有一个很好的解决方案吗?

答案 2 :(得分:1)

使用build构建对象,然后手动调用save ...

t = build(:test_model)
t.save