2011-05-21 76 views
47

我想测试运行我编写的迁移后的特定条件。目前最好的方法是什么?如何测试Rails迁移?

为了使这个具体:我做了一个迁移,将一列添加到模型,并给它一个默认值。但是我忘了更新该模型的所有预先存在的实例,以便为新列创建该默认值。我现有的测试都不会抓住这一点,因为它们都以新的数据库开始并添加新数据,这些数据将具有默认值。但是如果我推动生产,我知道事情会崩溃,我希望我的测试告诉我。我发现http://spin.atomicobject.com/2007/02/27/migration-testing-in-rails/,但还没有尝试过。这是非常古老的。这是最先进的吗?

+0

非常好的问题。 +1 – d11wtq 2011-05-21 02:22:51

+0

这看起来像一个有用的帖子:http://blog.carbonfive.com/2011/01/27/start-testing-your-migrations-right-now/ – maahd 2016-02-12 14:57:11

回答

3

我做了一个向模型添加一列的迁移,并给它一个默认值。但是我忘了更新该模型的所有预先存在的实例,以便为新列创建该默认值。

基于此声明,您只是试图测试一个“旧”模型,具有默认值,是否正确?

理论上,您正在测试导轨是否可用。即,“钢轨是否将默认值设置为新添加的列”

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

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

0

我不知道Rails的,但我认为这种方法是独立于模具 我用下面的方法相同:

  • 使数据库脚本的肯定部署版本apropiatly标记/标签版本控制
  • 根据您至少需要三个脚本:从头开始创建旧版本的脚本(1),从头开始创建新版本的脚本(2)以及从旧版本创建新版本的脚本版本(3)。
  • 创建两个db实例/ schemata。在一个运行脚本2中,在另一个运行脚本1后面跟着脚本3
  • 比较两个数据库中的结果,对数据字典使用sql查询。

为了测试也执行与1和3之间脚本2再次运行SQL查询后的实际数据,负荷测试数据到数据库中的效果,比较结果

15

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

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

这里有一个总结:

  1. 创建迁移照常
  2. 创建一个文件,把你的迁移测试中建议:test/unit/import_legacy_devices_migration_test.rbspec/migrations/import_legacy_devices_migration_spec.rb 注意:您可能需要显式地加载迁移文件轨道可能不会为你加载。像这样的东西应该做的:require File.join(Rails.root, 'db', 'migrate', '20101110154036_import_legacy_devices')
  3. 迁移是(像红宝石一切),就是一个类。测试updown方法。如果你的逻辑很复杂,我建议将一些逻辑重构成较小的方法,这样更容易测试。
  4. 调用up,设置一些一些数据,因为这将是您的迁移之前,并断言它的状态,你的期望之后之前。

我希望这会有所帮助。

UPDATE:自发布此消息以来,我在我的博客上发布了example migration test

UPDATE:这里有一个想法,即使在开发中运行它们之后,仍然可以测试迁移。

编辑:我已将我的概念证明更新为完整的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 
+0

这听起来像一个答案的一部分,但通常情况下,该设置从最新的开发数据库克隆数据库结构,在这里我们需要从早期的状态开始 - 理想情况下,新迁移从未运行过(而不是已经运行并恢复)。使测试数据库进入适当状态的便利方法是什么? – 2013-03-22 17:47:32

+0

@SteveJorgensen这是一个很好的问题。当我编写迁移测试时,我通常对它们进行TDD,然后在实际运行迁移后将其删除。他们不会坚持继续作为回归测试,因为正如您所说,一旦您迁移了,测试会反映新的迁移状态,并且迁移测试将失败。 非常抱歉,我从来没有处理过如何设置数据库。我有一个想法,我会尽快更新我的答案,一旦我有机会鼓捣它。 – 2013-03-23 01:08:34

+0

一些Google搜索引起了我一个有趣的相关文章:http://spin.atomicobject。com/2007/02/27/migration-testing-in-rails/ – 2013-03-23 08:30:36

0

你可以考虑运行测试套件的分离部分与特定的设置,对生产数据的副本(例如像yaml_db)。

这是一个有点元,如果你知道有什么潜在的问题与你的新迁移你很可能会更好只是增强他们掩盖您的特定需求,但它是可能的。

2

我只需要创建一个类的实例,然后调用它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