测试迁移时如何暂时禁用 "needs_migration?" 检查?

How temporarily disable "needs_migration?" check when testing migration?

我已经编写了规范来测试我的迁移,但是当我 运行 它时,我得到了一个错误:

ActiveRecord::PendingMigrationError:

Migrations are pending. To resolve this issue, run:

    bin/rake db:migrate RAILS_ENV=test

我试图在 before 部分禁用迁移检查,但该检查 运行ning 在所有测试之前。

如何禁用迁移检查以进行测试?

config/environments/test.rb中添加行

config.active_record.migration_error = false 

测试 Rails 迁移有点痛苦,所以我宁愿退后一步,想想这是否需要在 Rails 迁移中/在 Rails 迁移中测试.

基本上有两种不同类型的迁移

架构迁移

主要使用 Rails 内置函数。除非你做一些手工制作 SQL 我不会费心测试这个并相信这里的框架。

数据迁移

数据迁移用于回填或更改数据。由于数据是您最宝贵的资产之一,丢失或损坏它是非常痛苦的,所以我绝对建议为数据迁移编写测试。

如前所述,测试迁移有点麻烦,所以我会尝试在它自己的(服务)class 中抽象数据迁移代码。像

class DataMigration::UpdateUsername
  def self.run
    new.run
  end

  def run
    User.all do |batch|
      user.update(name: user.name.capitalize)
    end
  end
end

您现在可以像正常 class 一样测试数据迁移,如下所示:

it 'does capitalize the name' do
  user = create(:user, name: 'name')

  DataMigration::UpdateUsername.run

  expect(user.reload.name).to eq('NAME')
end

现在我们可以在 Rails 迁移中使用此 class 或者例如只需在 Rake 任务中使用它。在 Rake 任务中使用它还有一个优点,我们可以传入参数,运行 多个并行数据迁移(例如,你有一个大数据集),甚至在你不能在 Rails迁移。

示例

class DataMigration::UpdateUsername
  def initialize(start_id:, finish_id:)
    @start_id = start_id
    @finish_id = finish_id
  end

  def run
    User.find_in_batches(start: start_id, finish: finish_id) do |batch|
      batch.each do |user|
        user.update(name: user.name.capitalize)
      end
    end
  end
end

现在我们可以为此创建一个自定义任务

namespace :db do
  desc "Runs user data migration"
  task :update_user, [:start, :finish] do |task, args|
    DataMigration::UpdateUsername.new(start_id: args[:start], finish_id: args[:finish])
  end
end

rake db:update_user[0, 10000]
rake db:update_user[10000, 20000]
# ...