2016-05-19 5 views
0

移行ファイルをテストします。場所とユーザーの2つのテーブルがあります。場所has_manyのユーザー。テーブルはlocation_idによって接続されます。移行では、「場所」表の列「名前」に固有の制約を追加し、「場所」表の重複する場所名を持つ重複する行を削除し、すべてのユーザーにその場所の最初の出現箇所を指すようにします。Rails:移行ファイルのテスト

これは私の移行ファイルの一部です:

def self.remove_duplications 
    grouped = all.group_by{|location| [location.name] } 
    grouped.values.each do |duplicates| 
     # the first one we want to keep right? 
     first_one = duplicates.shift # or pop for last one 
     users = User.all 
     users.each do |user| 
     if user.location && user.location.name == first_one.name 
      user.location_id = first_one.id 
      user.save! 
     end 
     end 
     duplicates.each do |duplicate| 
     duplicate.destroy! 
     end 
    end 
    end 
    end 

    def self.up 
    Location.remove_duplications 
    remove_index :locations, column: :name 
    add_index :locations, :name, unique: true 
    end 

    def self.down 
    remove_index :locations, column: :name # remove unique index 
    add_index :locations, :name # adds just index, without unique 
    end 

どのように私はこれをテストすることができますか?それは手動でそれをテストするのが難しくなってきた。

+0

http://blog.carbonfive.com/2011/01/27/start-testing-your-migrations-right-now/ – p4sh4

+0

私はこれを見ていましたが、テスト用にRspecを使用していました。しかし、私はRspecを使用していません。その他の提案はありますか? – Abhishek

答えて

0

は、次の2つの異なる移行にこれを分割することによって開始する必要があります。ここに

class RemoveDuplicateLocations < ActiveRecord::Migration 
    def up 
    # @todo remove duplicate records ... 
    end 
end 

# is not very complex now 
class AddNameUniquenessToLocations < ActiveRecord::Migration 
    def change 
    remove_index :locations, column: :name 
    add_index :locations, :name, unique: true 
    end 
end 

理由は、それが非常に簡単にロジックを作成することで、はるかに少ないクレイジー・ロールバック。また、RemoveDuplicateLocationsの移行は本当に可逆的なものではありません。ロールバック内の重複したロケーションレコードを実際に再作成することはできません(少なくとも、データベースのバックアップに頼らなくても)。

次に、RemoveDuplicateLocationsをテストして実装する方法を調べることができます。これは、あなたがそれをテストすることができる方法の大まかな概要です:

require 'test_helper' 
require Rails.root.join('db', 'migrate', '20160519121310_remove_duplicate_locations.rb') 

class RemoveDuplicateLocationsTest < MiniTest::Unit::TestCase 

    let(:migration) { RemoveDuplicateLocations } 
    let(:previous_version) { "abc_123" # add previous state here } 

    # you may want to use factories or fixtures here 
    let(:locations) do 
    [ 
     Location.create(name: 'Paris') 
     Location.create(name: 'Paris') 
     Location.create(name: 'London') 
    ] 
    end 
    let(:users) { locations.map.do { |l| User.create(location: l) } } 

    def setup 
    ActiveRecord::Migrator.migrate(['db/migrate'], previous) 
    users # insert records by referencing lazy loading let var 
    end 

    def test_removes_duplicate_records 
    migration.up 
    duplicated_names = Location.group(:name) 
           .having("count(name) > 1") 
           .count.keys 
    assert_empty duplicate_names 
    assert_equals 2, Location.count 
    end 

    def test_relinks_user_location 
    migration.up 
    users = User.where(location: { name: 'Paris' }) 
    asset_equals 2, users.where(location: locations.first).count 
    asset_equals 0, users.where.not(location: locations.first).count 
    end 
end 

あなたは、移行を実装します:あなたはすべての場所を引っ張っていないので

class RemoveDuplicateLocations < ActiveRecord::Migration 
    def up 
    dupe_names = Location.group(:name).having("count(name) > 1").count.keys 
    dupe_names.map do |name| 
     dupes = Location.where(name: name).order(:id).ids 
     first = dupes.shift 
     User.where(location_id: dupes).update_all(location_id: first) 
     Location.destroy_all(id: dupes) 
    end 
    end 
end 

これはあなたの試みよりもはるかに少ないメモリを使用する必要がありますし、ユーザーをメモリに入れておく必要があります。ただし、場所の数が非常に多い場合でも問題は発生する可能性があります。

+0

テストもマイグレーションも保証されていません。これは、コピー貼り付けのソリューションではなく、出発点です。 – max

+0

最初にmigration.upを実行した後、2番目のテストを実行できるように移行する必要がありますか?もし私が間違っていれば私を正す? – Abhishek

+0

'ActiveRecord :: Migrator.migrate(previous)'は各テストの後に状態をリセットするべきです。作成したLocationレコードとUserレコードをロールバックするトランザクションフィクスチャを使用している場合は、工場とDatabaseCleanerを使用します。そうでなければ、ティアダウンでクリーンアップする必要があります。 – max

関連する問題