ruby-on-rails - migraciones - migracion en rails
¿Cómo pruebo las migraciones de Rails? (5)
Hice una migración que agrega una columna a un modelo y le da un valor predeterminado. Pero olvidé actualizar todas las instancias preexistentes de ese modelo para tener ese valor predeterminado para la nueva columna.
En base a esta afirmación, solo intentas probar que un modelo "viejo" tiene el valor predeterminado, ¿correcto?
Teóricamente estás probando si los rieles funcionan. Es decir, "¿Los rieles establecen un valor predeterminado para una columna recién agregada?"
Agregar una columna y establecer un valor predeterminado estará allí en los registros "antiguos" de su base de datos.
Entonces, no necesita actualizar los otros registros para reflejar la configuración predeterminada, entonces. En teoría, no hay nada que probar, ya que los rieles lo han probado por ti. Por último, la razón para usar los valores predeterminados es para no tener que actualizar las instancias previas para usar ese valor predeterminado, ¿verdad?
Quiero probar que ciertas condiciones se mantienen después de ejecutar una migración que he escrito. ¿Cuál es la mejor manera actual de hacer eso?
Para hacer esto concreto: realicé una migración que agrega una columna a un modelo y le da un valor predeterminado. Pero olvidé actualizar todas las instancias preexistentes de ese modelo para tener ese valor predeterminado para la nueva columna. Ninguna de mis pruebas actuales detectará eso, porque todas comienzan con una nueva base de datos y agregan nuevos datos, que tendrán el valor predeterminado. Pero si presiono para la producción, sé que las cosas se romperán, y quiero que mis pruebas me digan eso.
He encontrado http://spin.atomicobject.com/2007/02/27/migration-testing-in-rails/ , pero no lo he probado. Es muy viejo ¿Es ese el estado del arte?
No sé Rails, pero creo que el enfoque es el mismo independientemente de las herramientas. Utilizo el siguiente enfoque:
- asegúrese de que las versiones implementadas de las secuencias de comandos de la base de datos estén etiquetadas / etiquetadas de manera adecuada en el Control de versiones
- basado en eso necesita al menos tres scripts: un script que crea la versión anterior desde cero (1), un script que crea la nueva versión desde cero (2) y un script que crea la nueva versión desde la versión anterior (3) .
- crear dos instancias db / schemata En un script de ejecución 2, en el otro script de ejecución 1 seguido de script 3
- compare los resultados en las dos bases de datos, usando consultas SQL contra el diccionario de datos.
Para probar también el efecto en datos reales, cargue datos de prueba en las bases de datos después de ejecutar el script 2 y entre 1 y 3. De nuevo ejecute consultas SQL, compare los resultados
Podría considerar ejecutar partes aisladas de su suite de pruebas con configuraciones específicas en comparación con copias de sus datos de producción (con, por ejemplo, algo como yaml_db ).
Es un poco meta, y si sabes cuáles son los posibles problemas con tus nuevas migraciones, es mejor que los mejores para cubrir tus necesidades específicas, pero es posible.
Solo creo una instancia de la clase, luego la invoco up
o down
.
Por ejemplo:
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
Peter Marklund tiene un ejemplo de probar una migración aquí: https://gist.github.com/700194 (en rspec).
Nota: las migraciones han cambiado desde su ejemplo para usar métodos de instancia en lugar de métodos de clase.
Aquí hay un resumen:
- Crear una migración como de costumbre
- Cree un archivo para colocar su prueba de migración. Sugerencias:
test/unit/import_legacy_devices_migration_test.rb
ospec/migrations/import_legacy_devices_migration_spec.rb
NOTA: probablemente necesite cargar explícitamente el archivo de migración ya que los rieles probablemente no lo carguen por usted. Algo así debería hacer:require File.join(Rails.root, ''db'', ''migrate'', ''20101110154036_import_legacy_devices'')
- Las migraciones son (como todo en ruby), solo una clase. Pruebe los métodos
up
ydown
. Si su lógica es compleja, sugiero refactorizar bits de lógica a métodos más pequeños que serán más fáciles de probar. - Antes de llamar, configure algunos datos como lo harían antes de su migración, y afirme que su estado es lo que espera después.
Espero que esto ayude.
ACTUALIZACIÓN : Desde publicar esto, publiqué en mi blog un ejemplo de prueba de migración .
ACTUALIZACIÓN : He aquí una idea para probar migraciones incluso después de que se hayan ejecutado en desarrollo.
EDITAR : He actualizado mi prueba de concepto a un archivo de especificaciones completo utilizando el ejemplo artificial de mi publicación de blog.
# 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