2016-05-19 3 views
0

Ich möchte meine Migrationsdatei testen. Es gibt zwei Tabellen: Ort und Benutzer. Standort has_many Benutzer. Tabellen sind über location_id verbunden. Die Migration fügt einer Spalte "name" in der Tabelle "Location" eine eindeutige Integritätsregel hinzu und löscht doppelte Zeilen mit dem doppelten Standortnamen in der Tabelle "Location", sodass alle Benutzer auf das erste Vorkommen ihres Standorts verweisen.Rails: Testen der Migrationsdatei

Dies ist der Teil meiner Migration File:

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 

Wie kann ich das testen? Es wird schwierig, es manuell zu testen.

+0

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

+0

Ich hatte dies gesehen, aber es verwendet Rspec zum Testen. Aber ich benutze Rspec nicht. Irgendwelche anderen Vorschläge? – Abhishek

Antwort

0

Sie sollten durch Aufspalten dies in zwei verschiedene Wanderungen starten:

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 

Der hier Grund ist, dass es die Logik viel leichter macht und Rollbacks verrückt viel weniger. Auch die RemoveDuplicateLocations Migration ist nicht wirklich umkehrbar - Sie können diese doppelten Standortdatensätze in einem Rollback nicht wirklich neu erstellen (zumindest nicht ohne auf ein db-Backup zurückgreifen zu müssen).

Wir können dann beginnen zu prüfen, wie zu testen und zu implementieren RemoveDuplicateLocations. Dies ist in groben Zügen, wie Sie es testen konnte:

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 

Sie dann die Migration umsetzen würde:

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 

Dies sollte weit weniger Speicher als der Versuch verwenden, da Sie nicht alle Orte ziehen und Benutzer in den Speicher - Sie können immer noch Probleme haben, wenn die Anzahl der Standorte jedoch sehr groß ist.

+0

Beachten Sie, dass weder der Test noch die Migration garantiert ausgeführt werden! Dies ist als Ausgangspunkt gedacht - keine Kopierpastenlösung. – max

+0

Nach der ersten migration.up müssen wir nach unten migrieren, damit der zweite Test ausgeführt werden kann? Korrigiere mich, wenn ich falsch liege? – Abhishek

+0

'ActiveRecord :: Migrator.migrate (vorherige)' sollte den Zustand nach jedem Test zurücksetzen. Wenn Sie transaktionale Fixtures verwenden, die die erstellten Location- und User-Datensätze zurücksetzen sollen (ich denke, ich benutze Factories und DatabaseCleaner), sollten Sie ansonsten im Teardown aufräumen. – max