在Rails中加载自定义夹具

时间:2014-02-26 15:28:49

标签: ruby-on-rails ruby fixtures

我们正在尝试为一组使用与ActiveRecord :: Base不同的数据库连接的模型加载fixture(在本例中继承自Foo :: Base)。

我们创建了这个模块,我们将其包含在ActiveSupport :: TestCase中,并指定.yml文件的路径,例如foo_fitures :all。这适用于第一次运行的测试。定义了夹具访问器,并在数据库中找到记录。但是对于后续测试,数据库中没有记录。

module Foo::Fixtures
  extend ActiveSupport::Concern

  included do
    setup :setup_foo_fixtures
    teardown :teardown_foo_fixtures

    class_attribute :foo_fixture_path
    class_attribute :foo_fixture_table_names

    self.foo_fixture_table_names = []
  end

  module ClassMethods
    def foo_fixtures(*fixture_names)
      if fixture_names.first == :all
        fixture_names = Dir[foo_fixture_path.join("**/*.yml")].map { |f| File.basename(f, ".yml") }
      else
        fixture_names = fixture_names.flatten.map { |n| n.to_s }
      end

      self.foo_fixture_table_names |= fixture_names
      require_fixture_classes(fixture_names)
      setup_fixture_accessors(fixture_names)
    end
  end

  def setup_foo_fixtures
    @loaded_fixtures.merge!(load_foo_fixtures)
  end

  def teardown_foo_fixtures
    Foo::Base.clear_active_connections!
  end

  private

  def load_foo_fixtures
    foo_classes = Foo::Base.subclasses.flat_map { |klass| klass.abstract_class ? klass.subclasses : klass }
    class_names = foo_classes.each_with_object({}) do |klass, memo|
      memo[klass.table_name.to_sym] = klass if klass.table_name.present? && foo_fixture_table_names.include?(klass.table_name)
    end
    foo_fixtures = ActiveRecord::Fixtures.create_fixtures(foo_fixture_path, foo_fixture_table_names, class_names) do
      Foo::Base.connection
    end
    Hash[foo_fixtures.map { |f| [f.name, f] }]
  end
end

Rails的夹具系统有点复杂,我无法弄清楚我们缺少什么来确保我们的额外灯具已经加载。

1 个答案:

答案 0 :(得分:0)

行。看起来它可能是从数据库中删除我们的灯具的事务。我的猜测是,在我们的代码加载到灯具之前,事务已经开始了,所以这就是为什么他们在那里进行第一次测试,但是在第二次测试中出现了。

所以我们改变了策略,现在我们只是挂钩load_fixturesfixtures。这很好用。

module FooFixtures
  module ClassMethods
    def foo_fixture_classes
      collect_subclasses = ->(k) { k.abstract_class ? k.subclasses.flat_map(&collect_subclasses) : k }
      Foo::Base.subclasses.flat_map(&collect_subclasses)
    end

    def foo_fixture_path
      Rails.root.join("test/foo_fixtures")
    end

    def foo_fixture_table_names
      Dir[foo_fixture_path.join("**/*.yml")].map { |f| File.basename(f, ".yml") }
    end

    def fixtures(*fixture_names)
      super
      if fixture_names.first == :all
        require_fixture_classes(foo_fixture_table_names)
        setup_fixture_accessors(foo_fixture_table_names)
      end
    end
  end

  private

  def load_fixtures
    foo_fixture_path = self.class.foo_fixture_path
    foo_fixture_table_names = self.class.foo_fixture_table_names
    class_names = self.class.foo_fixture_classes.each_with_object({}) do |klass, memo|
      memo[klass.table_name.to_sym] = klass if klass.table_name.present? && foo_fixture_table_names.include?(klass.table_name)
    end
    foo_fixtures = ActiveRecord::Fixtures.create_fixtures(foo_fixture_path, foo_fixture_table_names, class_names) do
      Foo::Base.connection
    end

    super.merge(Hash[foo_fixtures.map { |f| [f.name, f] }])
  end
end

class ActiveSupport::TestCase
  extend FooFixtures::ClassMethods
  prepend FooFixtures

  self.foo_fixture_classes.each do |fixture_class|
    set_fixture_class fixture_class.table_name.to_sym => fixture_class
  end

  ...
end