我如何测试Rails迁移?

XZV*_*SFD 56 database testing ruby-on-rails

我想在运行我写过的迁移后测试某些条件是否成立.目前最好的方法是什么?

为了使这个具体:我进行了一次迁移,为模型添加了一个列,并给它一个默认值.但是我忘记更新该模型的所有预先存在的实例以获得新列的默认值.我现有的测试都没有捕获到它,因为它们都是从一个新数据库开始并添加新数据,这些数据将具有默认值.但如果我投入生产,我知道事情会破裂,我希望我的测试告诉我.

我找到了http://spin.atomicobject.com/2007/02/27/migration-testing-in-rails/,但还没试过.它很老了.这是最先进的吗?

Ami*_*tin 19

Peter Marklund在这里有一个测试迁移的例子:https://gist.github.com/700194(在rspec中).

注意,自他的示例以来,迁移已更改为使用实例方法而不是类方法.

这是一个总结:

  1. 像往常一样创建迁移
  2. 创建一个文件以进行迁移测试.建议:test/unit/import_legacy_devices_migration_test.rb或者spec/migrations/import_legacy_devices_migration_spec.rb 注意:您可能需要显式加载迁移文件,因为rails可能不会为您加载它.这样的事情应该做:require File.join(Rails.root, 'db', 'migrate', '20101110154036_import_legacy_devices')
  3. 迁移(就像ruby中的所有内容),只是一个类.测试updown方法.如果你的逻辑很复杂,我建议将一些逻辑重构为更容易测试的小方法.
  4. 在调用之前up,设置一些在迁移之前的数据,并声明它的状态是您之后所期望的.

我希望这有帮助.

更新:自发布此消息后,我在我的博客上发布了一个示例迁移测试.

更新:这是一个测试迁移的想法,即使它们已经在开发中运行.

编辑:我已经使用我博客文章中的人为设计示例将我的概念验证更新为完整的spec文件.

# spec/migrations/add_email_at_utc_hour_to_users_spec.rb
require 'spec_helper'

migration_file_name = Dir[Rails.root.join('db/migrate/*_add_email_at_utc_hour_to_users.rb')].first
require migration_file_name


describe AddEmailAtUtcHourToUsers do

  # This is clearly not very safe or pretty code, and there may be a
  # rails api that handles this. I am just going for a proof of concept here.
  def migration_has_been_run?(version)
    table_name = ActiveRecord::Migrator.schema_migrations_table_name
    query = "SELECT version FROM %s WHERE version = '%s'" % [table_name, version]
    ActiveRecord::Base.connection.execute(query).any?
  end

  let(:migration) { AddEmailAtUtcHourToUsers.new }


  before do
    # You could hard-code the migration number, or find it from the filename...
    if migration_has_been_run?('20120425063641')
      # If this migration has already been in our current database, run down first
      migration.down
    end
  end


  describe '#up' do
    before { migration.up; User.reset_column_information }

    it 'adds the email_at_utc_hour column' do
      User.columns_hash.should have_key('email_at_utc_hour')
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 注意 - 如果你使用`def change ...`进行可逆迁移,那么你必须用`migration.migrate(:down)`和`migration.migrate替换`migration.down`和`migration.up`. (:上)`.这应该也适用于上下. (3认同)
  • 这听起来像是答案的一部分,但通常情况下,设置会从最新的开发数据库克隆数据库结构,这里我们需要从较早的状态开始 - 最好是从未运行过新迁移的状态(而不是已经运行并恢复)。让测试数据库进入适当状态的便捷方法是什么? (2认同)

ian*_*nks 5

我只是创建该类的实例,然后在其上调用updown对其进行调用。

例如:

require Rails.root.join(
  'db',
  'migrate',
  '20170516191414_create_identities_ad_accounts_from_ad_account_identity'
)

describe CreateIdentitiesAdAccountsFromAdAccountIdentity do
  subject(:migration) { described_class.new }

  it 'properly creates identities_ad_accounts from ad account identities' do
    create_list :ad_account, 3, identity_id: create(:identity).id

    expect { suppress_output { migration.up } }
      .to change { IdentitiesAdAccount.count }.from(0).to(3)
  end
end
Run Code Online (Sandbox Code Playgroud)


pja*_*mer 3

我进行了迁移,将一列添加到模型中,并为其指定默认值。但我忘记更新该模型的所有预先存在的实例,以便为新列提供默认值。

根据这个声明,您只是想测试“旧”模型是否具有默认值,对吗?

理论上,您正在测试 Rails 是否有效。即,“rails 是否为新添加的列设置默认值”

添加列并设置默认值将存在于数据库的“旧”记录中。

因此,您不需要更新其他记录来反映默认设置。理论上没有什么可以测试的,因为 Rails 已经为你测试过了。最后,使用默认值的原因是您不必更新以前的实例即可使用该默认值,对吧?