如何将通用数据放入 Ruby-on-Rails 上的种子和装置中?

问题描述 投票:0回答:1

我想为 DB 初始化的种子记录和 Ruby-on-Rails 测试的固定装置分配相同的数据,而不违反 DRY 原则。

例如,让我们考虑这样的情况:模型

Genre
具有用户可编辑的属性
description
以及半永久固定的属性
mname
priority
,这些属性与模型和一些算法。至于后两者,它们可以(并且应该?)被定义为模型中的常量,并且种子和固定装置(很容易)都引用它,如下所示。

 # Model /app/models/genre.rb
PRIORITY = {novel: 3, poem: 5}.with_indifferent_access

 # Seeds /db/seeds.rb
s="novel"; Genre.create!(mname: s, priority: Genre::PRIORITY[s],
             description: "Various novels")
s="poem";  Genre.create!(mname: s, priority: Genre::PRIORITY[s],
             description: "Favourite poems")
 # Fixtures /test/fixtures/genres.yml
novel:
  mname: novel
  priority: <%= Genre::PRIORITY[:novel] %>
  description: "Various novels"

poem:
  mname: poem
  priority: <%= Genre::PRIORITY[:poem] %>
  description: "Favourite poems"

在此示例中,

description
的值在种子和夹具中单独定义,手动设置为相同的值。这违背了 DRY 原则。我宁愿将它们定义在一个地方,以便种子和赛程都可以毫无歧义地引用它们。

我应该把它们放在哪里以及如何参考它们?

与硬编码

priority
不同,恐怕模型中的常量(或
lib/
config/
中的代码)不是定义用户可编辑参数
description
初始值的正确位置,因为 (1) 它可以随时更改,并且 (2) 与运行应用程序无关。这些值仅与播种和测试相关(作为初始值),以便开发和测试环境中的输出看起来相似。

ruby-on-rails testing architecture fixtures seeding
1个回答
0
投票

OP 希望test数据库(DB)中的部分初始(夹具)数据尽可能接近真实(developmentproduct)DB,这将最大限度地减少仅在测试环境,或者相反,测试环境中出现的错误仅在实际情况下的其他环境中永远不会出现。另外,如果系统测试时在test环境中看到的截图与在development环境中看到的非常相似,那就让人放心了。

正如OP所说,数据库中与应用程序算法紧密相连的表数据应该在

app/
(或
conf/
)目录中定义,并在数据库初始化中读取,无论是用于测试 开发 环境。但是对于应用程序来说不是必需的数据,包括数据库初始化的一些或大部分种子,不应该是必需的。我认为
db/
下的目录,也许(用户创建的)
db/seeds/
是放置此类数据的合适位置。其他可能性可能包括
lib/
下的目录,例如
lib/assets/
.

使用带有模型Genre的OP案例,以下是实现此类案例场景的方法之一。 假设模型假设有一个 Genre 记录,其唯一属性

mname: "novel"
与固定的
priority
3 相关联,依此类推(无论是永久的还是在某些场合或条件下)。您首先创建一个目录
db/seeds/

型号

 ## Model /app/models/genre.rb
class Genre < ApplicationRecord
  # defines algorithm-related constants.
  PRIORITY = {novel: 3, poem: 5}.with_indifferent_access
end

db/中的种子数据(在模块中定义)
其中读取上述模型数据。

 ## Seeds /db/seeds/genre.rb
module Seeds
  module Genre
    # Data to seed (descriptions are defined here)
    SEED_DATA = [["novel", "Various novels"],
                 ["poem", "Favourite poems"]].map{ |ea|
      [ea[0], {mname: ea[0],
               priority: Genre::PRIORITY[ea[0]],
               description: ea[1], }.with_indifferent_access ]
    }.to_h.with_indifferent_access
  end
end

种子脚本(默认用于开发)
其中读取上述种子数据。

 ## Seeds /db/seeds.rb
require_relative "seeds/genre.rb"

Seeds::Genre::SEED_DATA.each_pair do |ek, ev|
  record = Genre.find_or_initialize_by(mname: ev[:mname]}
  next if record.new_record?
  # If a record with the mname exists, do nothing, so that
  # this seeding script can be run multiple times without reverting
  # deliberate changes in production back to the original.
  record.update!(ev)
end

测试环境设置

 ## /test/test_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
require 'rails/test_help'

Dir[Rails.root+"db/seeds/*.rb"].uniq.each do |seed_file|
  require seed_file
end

ActiveRecord::FixtureSet.context_class.include Seeds

用于测试的夹具数据
其中读取部分包含模型常量的上述种子数据。

 ## Fixtures /test/fixtures/genres.yml
<% Seeds::Genre::SEED_DATA.each_pair do |ename, ehash| %>
  <% ehash.each_pair do |ek, edata| %>
genre_<%= ename %>:
  <%= ek %>: <%= edata %>
  <% end %>
<% end %>

这里,钥匙是

  • db/seeds/
    中定义种子数据,其中一些数据是从模型中读取的,
  • 要求并包含test环境的种子数据,在
    /test/test_helper.rb
  • 中定义
  • 用于夹具读取种子数据,其中利用了 ERB 的功能。

注意:上面的示例将 Ruby 字符串的直接输入放入 YAML。一般来说,该值应该被处理为 YAML 安全的,尤其是可能包含换行符的长字符串。

有了这些,您可以保证种子和测试夹具之间的数据集(可能部分)相同,基本上消除了数据不一致的风险,同时保持将附加数据注册到夹具的灵活性,而且您还可以(有时很大)减少灯具中的线路数量。

请参阅 Rails Reference 了解有关夹具设置的详细信息。

© www.soinside.com 2019 - 2024. All rights reserved.