首页 > 解决方案 > 如何在 Rails 中只播种一次测试数据库?

问题描述

我有一个包含 180,000 个单词的大型词典,需要将其加载到数据库中才能运行我的应用程序,并且对测试很有用。不幸的是,这需要大约 30 分钟来为数据库播种。无论如何,是否只为数据库播种一次,或者甚至只为数据库的一个表播种并允许每次运行刷新其他表?

编辑:我最终使用activerecord-import来大大加快播种过程。现在需要 16 秒,而不是半小时。我还注意到在我的/spec/rails_helper.rb文件中我有以下内容:

  config.before(:suite) do
     Rails.application.load_seed # loading seeds
  end

很明显,我很久以前就添加了它并且忘记了它,因为这是我使用的一种模板 rails_helper 文件。将其注释掉意味着我不会每次都运行它,如果我确实需要重新设置种子,我可以,只需取消注释即可。

出于某种原因,我错误地认为 rspec 只是默认播种,事实并非如此。

标签: ruby-on-railsdatabaseruby-on-rails-5

解决方案


您可以使用新的 Rails 6 提高播种效率insert_all。这会使用单个创建多个记录,insert并且不会实例化模型。OTOH 它不做任何验证,所以要小心。

DictionaryWords.insert_all([
  { word: "foo" },
  { word: "bar" },
])

或者,使用activerecord-import


但最好不要写180,000字。

种子和固定装置的问题是它们“一刀切”。它们必须包含所有可能的开发和测试情况。它们很脆弱,对种子的一次更改可能会神秘地破坏许多对固定装置做出假设的测试。如果您需要重置数据库,种子将被吹走。

相反,使用工厂并在需要时创建所需的内容。使用Faker等库生成虚假但有效的数据。

例如...

# Assuming you have classes called Dictionary and DictionaryWord
factory :dictionary do
end

factory :dictionary_word do
  dictionary
  word { Faker::Lorem.unique.word }
end

然后在您的测试中根据需要创建单词。我在这里使用RSpec

let(:dictionary) { create(:dictionary) }
let!(:words) { create_list(:dictionary_word, 3, dictionary: dictionary) }

context 'when the word is in the dictionary' do
  let(:word) { words.sample }

  it 'finds the word' do
    expect( dictionary.exists?(word) ).to be_truthy
  end
end

context 'when the word is not in the dictionary' do
  let(:word) { "septemburary" }

  it 'does not find the word' do
    expect( dictionary.exists?(word) ).to be_falsey
  end
end

如果您需要更多的手动测试单词,请打开控制台并制作一些单词。

[1] pry(main)> FactoryBot.create_list(:dictionary_words, 100)

这不是特别有效,但你可能并不真的需要 180,000 字。



推荐阅读