Testing Rails migrations at GitLab
原文:https://docs.gitlab.com/ee/development/testing_guide/testing_migrations_guide.html
- When to write a migration test
- How does it work?
- Testing an
ActiveRecord::Migration
class - Testing a non-
ActiveRecord::Migration
class
Testing Rails migrations at GitLab
为了可靠地检查 Rails 迁移,我们需要针对数据库架构对其进行测试.
When to write a migration test
- 后期迁移(
/db/post_migrate
)和后台迁移(lib/gitlab/background_migration
) 必须执行迁移测试. - 如果您的迁移是数据迁移,那么它必须具有迁移测试.
- 如有必要,其他迁移可能会进行迁移测试.
How does it work?
在测试签名中添加:migration
标记,可以在spec/support/migration.rb
挂钩before
和after
运行一些自定义 RSpec.
before
钩子会将所有迁移还原到尚未迁移被测试的迁移点.
换句话说,我们的自定义 RSpec 挂钩将找到以前的迁移,并将数据库向下迁移到以前的迁移版本.
使用这种方法,您可以针对数据库架构测试迁移.
一个after
钩子将数据库迁移并 reinstitute 最新的架构版本,因此该方法不会影响后续的规格,并确保适当的隔离.
Testing an ActiveRecord::Migration
class
要测试ActiveRecord::Migration
类(即常规迁移db/migrate
或迁移后db/post_migrate
),您将需要手动require
迁移文件,因为它不会随 Rails 自动加载. 例:
require Rails.root.join('db', 'post_migrate', '20170526185842_migrate_pipeline_stages.rb')
Test helpers
table
使用table
助手为table
创建一个临时的ActiveRecord::Base
派生模型. FactoryBot 不应用于为迁移规范创建数据. 例如,要在projects
表中创建一条记录:
project = table(:projects).create!(id: 1, name: 'gitlab1', path: 'gitlab1')
migrate!
使用migrate!
帮助程序来运行正在测试的迁移. 它不仅将运行迁移,还将在schema_migrations
表中增加模式版本. 这是必要的,因为在after
钩中,我们会触发其余的迁移,并且我们需要知道从哪里开始. 例:
it 'migrates successfully' do
# ... pre-migration expectations
migrate!
# ... post-migration expectations
end
reversible_migration
Use the reversible_migration
helper to test migrations with either a change
or both up
and down
hooks. This will test that the state of the application and its data after the migration becomes reversed is the same as it was before the migration ran in the first place. The helper:
- 在向上迁移之前运行
before
期望. - Migrates up.
- 运行
after
期望. - Migrates down.
before
运行before
期望.
Example:
reversible_migration do |migration|
migration.before -> {
# ... pre-migration expectations
}
migration.after -> {
# ... post-migration expectations
}
end
Example database migration test
该规范测试了db/post_migrate/20170526185842_migrate_pipeline_stages.rb
迁移. 您可以在spec/migrations/migrate_pipeline_stages_spec.rb
找到完整的规范.
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170526185842_migrate_pipeline_stages.rb')
RSpec.describe MigratePipelineStages do
# Create test data - pipeline and CI/CD jobs.
let(:jobs) { table(:ci_builds) }
let(:stages) { table(:ci_stages) }
let(:pipelines) { table(:ci_pipelines) }
let(:projects) { table(:projects) }
before do
projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a')
jobs.create!(id: 1, commit_id: 1, project_id: 123, stage_idx: 2, stage: 'build')
jobs.create!(id: 2, commit_id: 1, project_id: 123, stage_idx: 1, stage: 'test')
end
# Test just the up migration.
it 'correctly migrates pipeline stages' do
expect(stages.count).to be_zero
migrate!
expect(stages.count).to eq 2
expect(stages.all.pluck(:name)).to match_array %w[test build]
end
# Test a reversible migration.
it 'correctly migrates up and down pipeline stages' do
reversible_migration do |migration|
# Expectations will run before the up migration,
# and then again after the down migration
migration.before -> {
expect(stages.count).to be_zero
}
# Expectations will run after the up migration.
migration.after -> {
expect(stages.count).to eq 2
expect(stages.all.pluck(:name)).to match_array %w[test build]
}
end
end
Testing a non-ActiveRecord::Migration
class
要测试非ActiveRecord::Migration
测试(后台迁移),您将需要手动提供所需的架构版本. 请向要在其中切换数据库架构的上下文中添加schema
标签.
如果未设置,则schema
默认为:latest
.
Example:
describe SomeClass, schema: 20170608152748 do
# ...
end
Example background migration test
该规范测试了lib/gitlab/background_migration/archive_legacy_traces.rb
背景迁移. 您可以在spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb
找到完整的规范
require 'spec_helper'
describe Gitlab::BackgroundMigration::ArchiveLegacyTraces, schema: 20180529152628 do
include TraceHelpers
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:builds) { table(:ci_builds) }
let(:job_artifacts) { table(:ci_job_artifacts) }
before do
namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1', namespace_id: 123)
@build = builds.create!(id: 1, project_id: 123, status: 'success', type: 'Ci::Build')
end
context 'when trace file exists at the right place' do
before do
create_legacy_trace(@build, 'trace in file')
end
it 'correctly archive legacy traces' do
expect(job_artifacts.count).to eq(0)
expect(File.exist?(legacy_trace_path(@build))).to be_truthy
described_class.new.perform(1, 1)
expect(job_artifacts.count).to eq(1)
expect(File.exist?(legacy_trace_path(@build))).to be_falsy
expect(File.read(archived_trace_path(job_artifacts.first))).to eq('trace in file')
end
end
end
注意:由于我们使用删除数据库清除策略,因此这些测试不在数据库事务中运行. 不要依赖存在的事务.