94 lines
3.1 KiB
Markdown
94 lines
3.1 KiB
Markdown
|
# Testing migrations
|
||
|
|
||
|
In order to reliably test a migration, we need to test it against a database
|
||
|
schema that this migration has been written for. In order to achieve that we
|
||
|
have some _migration helpers_ and RSpec test tag, called `:migration`.
|
||
|
|
||
|
If you want to write a test for a migration consider adding `:migration` tag to
|
||
|
the test signature, like `describe SomeMigrationClass, :migration`.
|
||
|
|
||
|
## How does it work?
|
||
|
|
||
|
Adding a `:migration` tag to a test signature injects a few before / after
|
||
|
hooks to the test.
|
||
|
|
||
|
The most important change is that adding a `:migration` tag adds a `before`
|
||
|
hook that will revert all migrations to the point that a migration under test
|
||
|
is not yet migrated.
|
||
|
|
||
|
In other words, our custom RSpec hooks will find a previous migration, and
|
||
|
migrate the database **down** to the previous migration version.
|
||
|
|
||
|
With this approach you can test a migration against a database schema that this
|
||
|
migration has been written for.
|
||
|
|
||
|
Use `migrate!` helper to run the migration that is under test.
|
||
|
|
||
|
The `after` hook will migrate the database **up** and reinstitutes the latest
|
||
|
schema version, so that the process does not affect subsequent specs and
|
||
|
ensures proper isolation.
|
||
|
|
||
|
## Testing a class that is not an ActiveRecord::Migration
|
||
|
|
||
|
In order to test a class that is not a migration itself, you will need to
|
||
|
manually provide a required schema version. Please add a `schema` tag to a
|
||
|
context that you want to switch the database schema within.
|
||
|
|
||
|
Example: `describe SomeClass, :migration, schema: 20170608152748`.
|
||
|
|
||
|
## Available helpers
|
||
|
|
||
|
Use `table` helper to create a temporary `ActiveRecord::Base` derived model
|
||
|
for a table.
|
||
|
|
||
|
Use `migrate!` helper to run the migration that is under test. It will not only
|
||
|
run migration, but will also bump the schema version in the `schema_migrations`
|
||
|
table. It is necessary because in the `after` hook we trigger the rest of
|
||
|
the migrations, and we need to know where to start.
|
||
|
|
||
|
See `spec/support/migrations_helpers.rb` for all the available helpers.
|
||
|
|
||
|
## An example
|
||
|
|
||
|
```ruby
|
||
|
require 'spec_helper'
|
||
|
|
||
|
# Load a migration class.
|
||
|
|
||
|
require Rails.root.join('db', 'post_migrate', '20170526185842_migrate_pipeline_stages.rb')
|
||
|
|
||
|
describe MigratePipelineStages, :migration 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 the 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
|
||
|
end
|
||
|
```
|
||
|
|
||
|
## Best practices
|
||
|
|
||
|
1. Note that this type of tests do not run within the transaction, we use
|
||
|
a truncation database cleanup strategy. Do not depend on transaction being
|
||
|
present.
|