【问题标题】:Loading a Database of Fixtures in Rails在 Rails 中加载夹具数据库
【发布时间】:2018-09-03 13:38:43
【问题描述】:

我正在构建一个 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 语法来解释它们,但我对 那 不是很熟悉,所以我的理解仍然相当模糊的。我相信数据库是 使用 PostgreSQL 管理,但我很少需要通过它直接与它进行交互,所以我不是特别有经验。

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

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

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

【问题讨论】:

    标签: ruby-on-rails postgresql rspec-rails database-cleaner


    【解决方案1】:

    DatabaseCleaner 不适用于固定装置。它旨在与工厂一起使用。 ActiveRecord::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
    

    【讨论】:

    • 感谢您的回答!事实上,固定装置和工厂是不同的东西,我碰巧在我认为合适的时候使用它们。一些测试(比如从头开始创建内容)感觉更像“工厂”,而一些测试感觉更像“夹具”(我想要测试的行为感觉取决于数据库的内容)。我会阅读您链接的页面,如果我仍然不清楚该怎么做,我会回到这里。
    • 我真的不会混合使用它们——你可以同时使用它们来实现相同的目标,但在同一个项目中使用它们不会很好。您实际上只是消除了固定装置的唯一好处,即简单/速度。特别是因为使用 DatabaseCleaner 与 ActiveRecord::Fixtures 不兼容,会造成混乱。
    • 我明白了。我想我更喜欢使用固定装置,因为我对测试感兴趣的大多数行为感觉就像它以一种关键方式依赖于数据库结构,这使得基于工厂的测试难以编写。但是在某些行为更简单的地方,使用工厂感觉更好,因为这样我知道夹具的任何特殊功能都不会使测试通过“巧合”。我将尝试从我的项目中删除 DBCleaner,看看回滚是否按我想要的方式工作。
    • 顺便问一下,是什么让它们“不兼容”?
    • 我在问题中添加了一些细节(以避免混淆 cmets)
    猜你喜欢
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-27
    • 2015-01-01
    • 2012-01-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多