# Testing Rails migrations at GitLab

> 原文：[https://docs.gitlab.com/ee/development/testing_guide/testing_migrations_guide.html](https://docs.gitlab.com/ee/development/testing_guide/testing_migrations_guide.html)

*   [When to write a migration test](#when-to-write-a-migration-test)
*   [How does it work?](#how-does-it-work)
*   [Testing an `ActiveRecord::Migration` class](#testing-an-activerecordmigration-class)
    *   [Test helpers](#test-helpers)
        *   [`table`](#table)
        *   [`migrate!`](#migrate)
        *   [`reversible_migration`](#reversible_migration)
    *   [Example database migration test](#example-database-migration-test)
*   [Testing a non-`ActiveRecord::Migration` class](#testing-a-non-activerecordmigration-class)
    *   [Example background migration test](#example-background-migration-test)

# Testing Rails migrations at GitLab[](#testing-rails-migrations-at-gitlab "Permalink")

为了可靠地检查 Rails 迁移，我们需要针对数据库架构对其进行测试.

## When to write a migration test[](#when-to-write-a-migration-test "Permalink")

*   后期迁移（ `/db/post_migrate` ）和后台迁移（ `lib/gitlab/background_migration` ） **必须**执行迁移测试.
*   如果您的迁移是数据迁移，那么它**必须**具有迁移测试.
*   如有必要，其他迁移可能会进行迁移测试.

## How does it work?[](#how-does-it-work "Permalink")

在测试签名中添加`:migration`标记，可以在[`spec/support/migration.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/f81fa6ab1dd788b70ef44b85aaba1f31ffafae7d/spec/support/migration.rb)挂钩`before`和`after`运行一些自定义 RSpec.

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

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

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

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

## Testing an `ActiveRecord::Migration` class[](#testing-an-activerecordmigration-class "Permalink")

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

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

### Test helpers[](#test-helpers "Permalink")

#### `table`[](#table "Permalink")

使用`table`助手为`table`创建一个临时的`ActiveRecord::Base`派生模型. [FactoryBot](best_practices.html#factories) **不**应用于为迁移规范创建数据. 例如，要在`projects`表中创建一条记录：

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

#### `migrate!`[](#migrate "Permalink")

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

```
it 'migrates successfully' do
  # ... pre-migration expectations

  migrate!

  # ... post-migration expectations
end 
```

#### `reversible_migration`[](#reversible_migration "Permalink")

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:

```
reversible_migration do |migration|
  migration.before -> {
    # ... pre-migration expectations
  }

  migration.after -> {
    # ... post-migration expectations
  }
end 
```

### Example database migration test[](#example-database-migration-test "Permalink")

该规范测试了[`db/post_migrate/20170526185842_migrate_pipeline_stages.rb`](https://gitlab.com/gitlab-org/gitlab-foss/blob/v11.6.5/db/post_migrate/20170526185842_migrate_pipeline_stages.rb)迁移. 您可以在[`spec/migrations/migrate_pipeline_stages_spec.rb`](https://gitlab.com/gitlab-org/gitlab-foss/blob/v11.6.5/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[](#testing-a-non-activerecordmigration-class "Permalink")

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

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

Example:

```
describe SomeClass, schema: 20170608152748 do
  # ...
end 
```

### Example background migration test[](#example-background-migration-test "Permalink")

该规范测试了[`lib/gitlab/background_migration/archive_legacy_traces.rb`](https://gitlab.com/gitlab-org/gitlab-foss/blob/v11.6.5/lib/gitlab/background_migration/archive_legacy_traces.rb)背景迁移. 您可以在[`spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb`](https://gitlab.com/gitlab-org/gitlab-foss/blob/v11.6.5/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 
```

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