如何在 Rails 中只播种一次 Test db?

Mac*_*Mac 0 database ruby-on-rails ruby-on-rails-5

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

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

  config.before(:suite) do
     Rails.application.load_seed # loading seeds
  end
Run Code Online (Sandbox Code Playgroud)

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

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

Sch*_*ern 5

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

DictionaryWords.insert_all([
  { word: "foo" },
  { word: "bar" },
])
Run Code Online (Sandbox Code Playgroud)

或者,使用activerecord-import


不过18万字最好不要。

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

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

例如...

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

factory :dictionary_word do
  dictionary
  word { Faker::Lorem.unique.word }
end
Run Code Online (Sandbox Code Playgroud)

然后在您的测试中根据需要创建单词。我在这里使用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
Run Code Online (Sandbox Code Playgroud)

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

[1] pry(main)> FactoryBot.create_list(:dictionary_words, 100)
Run Code Online (Sandbox Code Playgroud)

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