我正在尝试使遗留的Rails系统达到更新的标准,但是在使测试数据库反映schema.rb
的状态以及通过迁移所做的更改时遇到了问题。
运行rake minitest:all
调用的代码与rake db:schema:load
相同吗?
环境
- 导轨 3.2.20
- 红宝石 1.9.3
- 迷你测试宝石 4.6.2
- 最小测试轨 0.5.2
- MySQL 5.1
- 本地操作系统 X、测试和生产 Linux
原始系统状态
该系统最初是由非软件工程师、不了解 Rails 等的人建立的。 添加了几个特定于MySQL的类型(例如unsigned int
(,这些类型在Rails的迁移和schema.rb
中没有特别支持。 所以系统使用了structure.sql
,并且对如何保持更新、签入 git 等等非常草率。
此外,在稍后的某个时候,有人决定将一些常用的数字、自动递增的主键id
字段替换为包含自生成 GUID 的varchar
。 字段名称仍id
,但它是新的数据类型。
但是所有数百个测试(他们确实编写了很多测试(都是根据 id 而不是夹具名称编写的,并且夹具之间存在依赖关系等。 他们没有更新测试,而是决定坚持使用旧架构(带有数字id
(进行测试,并使用新架构(在 id
中使用 varchar GUID(进行生产。
OP深吸一口气...
各种环境的数据库不同步,有数百个迁移,但它们停止工作,因为"开发"数据库是共享的,并且......你知道,这是一团糟。
我正在尝试解决所有这些问题,并最终迁移到PostgreSQL。
我做了什么
我使用 RAILS_ENV=production rake db:structure:dump
从本地生产数据库中转储架构 - 这产生了一个权威structure.sql
。 我创建了一个新的开发数据库,并使用rake db:structure:load
加载了它的模式 - 我仔细比较了生产和开发模式,它们是相同的,甚至是我上面提到的特定于MySQL的无符号int。
我想转向使用schema.rb
有两个原因。 首先,我想进入一个不依赖于MySQL的系统。 其次,使用structure.sql
时,我们会签入一个文件,该文件具有 自动增量值,数据库设置和运行最新db:migrate
的任何机器的其他特征 。 这可能会产生我宁愿避免的问题。
因此,我在本地将配置设置从:sql
更改为使用:ruby
设置来生成schema.rb
。
但是schema.rb
真的不喜欢将id
字段变成varchar
的想法 - 我已经确保使用此声明的所有模型都self.primary_key = :id
,然后我创建了一个新的迁移来替换所有旧的迁移,一个"汇总迁移",其内容主要是新schema.rb
, 但以多种方式进行了修改。
特别是,其中id
字段是 GUID varchar
我像这样设置表(从迁移中(:
class RolledUpStateAsOf20150403 < ActiveRecord::Migration
def up
# ... all other table definitions in the system
create_table "users", :id => false, :force => false do |t|
t.string "id", :limit => 36, :default => "", :null => false
t.string "login"
# and all the other user fields
end
execute("ALTER TABLE users ADD PRIMARY KEY (id);")
#...
end
def down
raise ActiveRecord::IrreversibleMigration
end
end
所以:
-
:id => false
防止迁移创建普通 ID -
:force => false
确保此迁移不会破坏生产数据库 - 具有大小和其他类似主键设置的
t.string "id"
- 最后
execute(ALTER TABLE ...)
将 id 声明为主键
每次我将来创建新的迁移时,schema.rb
都会更新 - 它现在不会准确(直到我们稍后删除那些古怪的id
字段(。 迁移将遵循正常的 Rails 实践。
一旦生产、暂存、开发和其他数据库同步,那么一切都很好。
除非我们测试。
那么为什么当我运行 Minitest 时这不起作用?
我正在使用最小测试运行测试,如下所示:
RAILS_ENV=test rake db:drop
RAILS_ENV=test rake db:create
RAILS_ENV=test rake db:migrate
RAILS_ENV=test rake minitest:all
但是后来我开始看到错误,错误是由于id
列被定义为int
而不是varchar
。
如果我在运行 minitest 之前检查架构(在 drop
、create
和我的魔术迁移之后(,这是正确的:时髦的主键会根据需要varchar
。
但是在测试过程中的某个地方,模式似乎被改回了Rails标准。 我可以返回并检查 ID 列返回到int
的架构。
据推测,该架构是从实际schema.rb
生成的。
这是正常/预期的行为吗? 关于如何实现我的目标的任何建议,这些建议很简单:
- 迁移再次有效
- 开发、测试、暂存、生产数据库在结构上是相同的
- 我现在需要忍受疯狂
varchar
基于 GUID 的id
字段 - 如果可能的话,我宁愿不使用
structure.sql
好的,所以我相信以下内容是正确的:迷你测试,或测试运行db:schema:load
...或在RAILS_ENV=test
中运行时db:structure:load
- 这可以通过运行rake test --trace
或rake minitest:all --trace
来观察。
因此,由于奇怪的使用varchar
而不是int
名为id
的字段,我的schema.rb
不会完全重新创建数据库......我不得不恢复使用structure.sql
。
解决方案的其余部分,包括将手动修改的schema.rb
版本添加为第一次"汇总"迁移,效果很好,后续迁移也很好。 在未来的某个时候,我会将 id 变回它们的自然形式;目前,这是一个合适的解决方案,如果不是优雅的话。