Testing Rails migrations at GitLab

原文:https://docs.gitlab.com/ee/development/testing_guide/testing_migrations_guide.html

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挂钩beforeafter运行一些自定义 RSpec.

before钩子会将所有迁移还原到尚未迁移被测试的迁移点.

换句话说,我们的自定义 RSpec 挂钩将找到以前的迁移,并将数据库向下迁移到以前的迁移版本.

使用这种方法,您可以针对数据库架构测试迁移.

一个after钩子将数据库迁移 reinstitute 最新的架构版本,因此该方法不会影响后续的规格,并确保适当的隔离.

Testing an ActiveRecord::Migration class

要测试ActiveRecord::Migration类(即常规迁移db/migrate或迁移后db/post_migrate ),您将需要手动require迁移文件,因为它不会随 Rails 自动加载. 例:

  1. require Rails.root.join('db', 'post_migrate', '20170526185842_migrate_pipeline_stages.rb')

Test helpers

table

使用table助手为table创建一个临时的ActiveRecord::Base派生模型. FactoryBot 应用于为迁移规范创建数据. 例如,要在projects表中创建一条记录:

  1. project = table(:projects).create!(id: 1, name: 'gitlab1', path: 'gitlab1')

migrate!

使用migrate! 帮助程序来运行正在测试的迁移. 它不仅将运行迁移,还将在schema_migrations表中增加模式版本. 这是必要的,因为在after钩中,我们会触发其余的迁移,并且我们需要知道从哪里开始. 例:

  1. it 'migrates successfully' do
  2. # ... pre-migration expectations
  3. migrate!
  4. # ... post-migration expectations
  5. 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:

  1. 向上迁移之前运行before期望.
  2. Migrates up.
  3. 运行after期望.
  4. Migrates down.
  5. before运行before期望.

Example:

  1. reversible_migration do |migration|
  2. migration.before -> {
  3. # ... pre-migration expectations
  4. }
  5. migration.after -> {
  6. # ... post-migration expectations
  7. }
  8. end

Example database migration test

该规范测试了db/post_migrate/20170526185842_migrate_pipeline_stages.rb迁移. 您可以在spec/migrations/migrate_pipeline_stages_spec.rb找到完整的规范.

  1. require 'spec_helper'
  2. require Rails.root.join('db', 'post_migrate', '20170526185842_migrate_pipeline_stages.rb')
  3. RSpec.describe MigratePipelineStages do
  4. # Create test data - pipeline and CI/CD jobs.
  5. let(:jobs) { table(:ci_builds) }
  6. let(:stages) { table(:ci_stages) }
  7. let(:pipelines) { table(:ci_pipelines) }
  8. let(:projects) { table(:projects) }
  9. before do
  10. projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
  11. pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a')
  12. jobs.create!(id: 1, commit_id: 1, project_id: 123, stage_idx: 2, stage: 'build')
  13. jobs.create!(id: 2, commit_id: 1, project_id: 123, stage_idx: 1, stage: 'test')
  14. end
  15. # Test just the up migration.
  16. it 'correctly migrates pipeline stages' do
  17. expect(stages.count).to be_zero
  18. migrate!
  19. expect(stages.count).to eq 2
  20. expect(stages.all.pluck(:name)).to match_array %w[test build]
  21. end
  22. # Test a reversible migration.
  23. it 'correctly migrates up and down pipeline stages' do
  24. reversible_migration do |migration|
  25. # Expectations will run before the up migration,
  26. # and then again after the down migration
  27. migration.before -> {
  28. expect(stages.count).to be_zero
  29. }
  30. # Expectations will run after the up migration.
  31. migration.after -> {
  32. expect(stages.count).to eq 2
  33. expect(stages.all.pluck(:name)).to match_array %w[test build]
  34. }
  35. end
  36. end

Testing a non-ActiveRecord::Migration class

要测试非ActiveRecord::Migration测试(后台迁移),您将需要手动提供所需的架构版本. 请向要在其中切换数据库架构的上下文中添加schema标签.

如果未设置,则schema默认为:latest .

Example:

  1. describe SomeClass, schema: 20170608152748 do
  2. # ...
  3. end

Example background migration test

该规范测试了lib/gitlab/background_migration/archive_legacy_traces.rb背景迁移. 您可以在spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb找到完整的规范

  1. require 'spec_helper'
  2. describe Gitlab::BackgroundMigration::ArchiveLegacyTraces, schema: 20180529152628 do
  3. include TraceHelpers
  4. let(:namespaces) { table(:namespaces) }
  5. let(:projects) { table(:projects) }
  6. let(:builds) { table(:ci_builds) }
  7. let(:job_artifacts) { table(:ci_job_artifacts) }
  8. before do
  9. namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
  10. projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1', namespace_id: 123)
  11. @build = builds.create!(id: 1, project_id: 123, status: 'success', type: 'Ci::Build')
  12. end
  13. context 'when trace file exists at the right place' do
  14. before do
  15. create_legacy_trace(@build, 'trace in file')
  16. end
  17. it 'correctly archive legacy traces' do
  18. expect(job_artifacts.count).to eq(0)
  19. expect(File.exist?(legacy_trace_path(@build))).to be_truthy
  20. described_class.new.perform(1, 1)
  21. expect(job_artifacts.count).to eq(1)
  22. expect(File.exist?(legacy_trace_path(@build))).to be_falsy
  23. expect(File.read(archived_trace_path(job_artifacts.first))).to eq('trace in file')
  24. end
  25. end
  26. end

注意:由于我们使用删除数据库清除策略,因此这些测试不在数据库事务中运行. 不要依赖存在的事务.