在 Rails 中加载灯具数据库

Loading a Database of Fixtures in Rails

我正在构建一个 Rails 应用程序(我是新手,如果某些措辞笨拙,请原谅我)。我正在尝试编写从数据库中提取和使用数据的测试(使用 RSpec),但我无法以简洁的方式编写测试。

一些测试(例如注册用户或创建内容)似乎最适合拥有新的数据库,而有些则需要数据库填充的固定装置。

目前我正在使用数据库清理器gem,配置如下:

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
    DatabaseCleaner.strategy = :truncation   end

  # start the transaction strategy as examples are run    
   config.around(:each) do |example|
     DatabaseCleaner.cleaning do
       example.run
     end    
   end

我对这两种策略都使用 truncation 的原因是我更喜欢在示例之间完全刷新 id 值(这样如果我在一次测试中 create,并且然后 create 在第二个测试中,第二个示例的 ID 应该是 1 而不是 2)。我不确定各种策略的确切含义 - 我发现 this question 似乎用 SQL 语法来解释它们,但我不太熟悉 that 所以我的理解还是比较模糊。我相信数据库是 使用 PostgreSQL 管理,但我很少需要通过它直接与它交互,所以我不是特别有经验。

所以我的数据库在每个示例之间完全被丢弃并从头开始构建 - 如果我想要一个干净的数据库那么这是理想的,但如果我只想加载固定装置那么它可能需要一段时间来创建所有楷模。感觉我应该能够拥有一个 'cached' 版本的装置,我可以为那些适合的示例加载它。但我不知道该怎么做,如果可能的话。有办法吗?

编辑: 根据评论中的讨论,我怀疑我可能想要删除数据库清理器并改用默认的 Rails 固定装置。我已经尝试过这个,我遇到的唯一问题与我在上面描述的 transaction 策略中遇到的问题相同。也就是说:当回滚测试创建的记录时,id 不会回滚,这是一种尴尬的行为。如果我为了 运行 测试的目的创建了一个 user,那么将它称为 User.find(1) 是很方便的,如果 id 不重置,这是不可能的。

这可能是某种危险信号,我不应该这样做(我愿意做其他事情)。我也意识到我可以只说 User.first 来获得相同的行为,这可能会更好。我不确定什么是合适的。

DatabaseCleaner 不适用于固定装置。它旨在与工厂一起使用。 ActiveRecord::Fixtures 有自己的回滚机制。

观念上的差别真的很大。

Fixtures 就像这个巨大的静态虚拟数据集,每个示例都被扔进数据库,然后通过事务重置。 fixtures 的一大缺点是你拥有的 fixtures 越多,应用程序的初始状态就越复杂,它鼓励测试和 fixtures 本身之间的紧密耦合。

这是一个示例,展示了值 "Marko Anastasov" 如何从代码之外的某处神奇地出现:

RSpec.describe User do
  fixtures :all

  describe "#full_name" do
    it "is composed of first and last name" do
      user = users(:marko)
      expect(user.full_name).to eql "Marko Anastasov"
    end
  end
end

虽然固定装置最近由于人们认为的简单性(以及 Minitest)而重新流行起来。

工厂 是产生唯一记录的对象工厂。您不必让一堆垃圾四处飘荡,而是以空白状态开始每个示例,然后使用工厂用确切的状态填充数据库以复制您正在测试的场景。如果做得好,可以最大限度地减少测试顺序问题、抖动测试和更改夹具破坏测试。

RSpec.describe User do
  describe "#full_name" do
    it "is composed of first and last name" do
      user = FactoryBot.create(:user)
      expect(user.full_name).to eql "#{user.first_name} #{user.last_name}"
    end
  end
end

这是一个生成伪随机值的好工厂的例子:

require 'ffaker'

FactoryBot.define do
  factory :user do
    first_name { FFaker::Name.first_name }
    last_name { FFaker::Name.last_name }
  end
end