Creación de Fábricas Automatizadas con FactoryBot y Faker en Rails
Clase 8 de 34 • Curso Intermedio de Ruby on Rails
Objetivo
Poder tener todas nuestra fábricas dinámicas de información completamente definidas para empezar a ejecutar la creación de todas las pruebas de nuestro sistema, en esta clase, usaremos FactoryBot y Faker, gemas que habíamos agregado en anteriores clases.
Cada fábrica tendrá la capacidad de generar registros de base de datos de forma automatizada y configurable, para que no tengamos la necesidad de crearlos a mano, esto involucra incluso registros que están relacionados con otros registros por relaciones de base de datos u otros.
Fábrica de Categorías
Para empezar a crear nuestra fábrica, podemos usar el siguiente generador en la raíz del proyecto
$ rails g factory_bot:model category
Este comando generará un archivo de nombre spec/factories/categories.rb
en este colocaremos el siguiente contenido
# spec/factories/categories.rb FactoryBot.define do factory :category do sequence(:name) { |n| "Categoria #{n}" } description { Faker::Lorem.sentence } end end
Lo que estamos haciendo en esta fábrica es automatizar la creación de una categoría con los campos name
y description
mientras en el name estamos usando un sistema de secuenciación de base de datos propio del FactoryBot para evitar que se creen dos categorías con el mismo nombre, en el campo de descripción estamos usando Faker para generar un texto de tipo Lorem Ipsum de forma aleatoria
Fábrica de Usuarios
Usemos el generador para crear la fábrica de usuario
$ rails g factory_bot:model user
Cambiemos el contenido completo del archivo creado por el generador de nombre spec/factories/users.rb
# spec/factories/users.rb FactoryBot.define do factory :user do sequence(:email) { |n| "person#{n}@example.com" } password { '123456' } end end
De igual forma, en la fábrica del usuario estamos usando el sistema de secuenciación del FactoryBot, y también estamos definiendo una contraseña estática con valor 123456
Fábrica de Participantes
Usemos el generador para la fábrica de participantes
$ rails g factory_bot:model participant
Cambiemos el contenido completo del archivo creado por el generador de nombre spec/factories/participants.rb
# spec/factories/participants.rb FactoryBot.define do factory :participant do association :user trait :responsible do role { Participant::ROLES[:responsible] } end trait :follower do role { Participant::ROLES[:follower] } end after(:build) do |participant, _| participant.user.save end end end
En esta fábrica hemos añadido tres elementos importantes, el sistema de asociación, el sistema de rasgos (traits) y el sistema de callbacks.
Con la definición del método association
FactoryBot de forma automática buscará la fábrica de Usuarios para construir el registro de usuario y asociarlo al registro de participante.
Con los rasgos, podemos de forma semántica definir al momento de invocar la fábrica si queremos que el participante tenga una condición específica, en este caso, que su rol sea responsable o seguidor.
Finalmente, con el sistema de callbacks, podemos ejecutar una o varias operaciones cuando el proceso de creación o construcción empiece o termine, en nuestro ejemplo, ejecutaremos el proceso participant.user.save
cuando el registro participant termine de construirse asegurando que el registro de usuario si sea persistido.
Fábrica de Tareas
Usemos el generador para la fábrica de tareas
$ rails g factory_bot:model task
Cambiemos el contenido completo del archivo creado por el generador de nombre spec/factories/tasks.rb
# spec/factories/tasks.rb FactoryBot.define do factory :task do sequence(:name) { |n| "Tarea #{n}" } sequence(:description) { |n| "Descripcion #{n}" } due_date { Date.today + 15.days } category association :owner, factory: :user factory :task_with_participants do transient do participants_count { 5 } end after(:build) do |task, evaluator| task.participating_users = build_list( :participant, evaluator.participants_count, task: task, role: 1 ) task.category.save task.owner.save end end end end
A pesar de que parezca complejo, en esta fábrica hemos combinado todos los elementos vistos en las anteriores fábricas y hemos añadido dos conceptos nuevos, el de asociación implícita con la llamada simple al método category y el concepto de fábrica anidada.
La asociación implícita a través del método category
simplemente hace lo mismo que la asociación de usuario en la fábrica de participantes, está creando un registro de categoría de forma automática cuando la tarea se está creando. Por otro lado, hay una declaración explícita de la fábrica de usuario en la relación owner
, la cual se comporta de igual forma que una asociación normal pero definiendo que la fábrica a usar será :user
La fábrica anidada, nos está dando la flexibilidad de complementar los elementos de la fábrica de tareas principal, dotandonos de la capacidad de crear una tarea con participantes, e incluso poder definir el número de participantes en la tarea construida a través del método transient
y la declaración de la variable participants_count
que más adelante será accedida en el cuerpo de callback desde un elemento de habilitación de transferencia de datos llamado evaluator
Internamente, en el cuerpo del callback, estamos usando un método de FactoryBot llamado build_list
que nos permitirá en un sólo comando crear una lista de registros con una fábrica definida, al final de esta ejecución simplemente estamos persistiendo las asociaciones generadas para el creador de la tarea y la categoría.
Probando nuestras fábricas
La mejor manera de probar nuestras fábricas es con la creación de una prueba automatizada para nuestro proyecto, así que creemos una prueba sencilla para nuestro modelo task, usando el generador de Rails
$ rails g rspec:model task
El contenido del archivo creado spec/models/task_spec.rb
debemos cambiarlo por el siguiente
# spec/models/task_spec.rb require 'rails_helper' RSpec.describe Task, type: :model do describe '#save' do let(:participants_count) { 4 } subject(:task) { build(:task_with_participants, participants_count: participants_count) } it 'is persisted' do expect(task.save).to eq true end context 'after save' do before(:each) { task.save } it 'has all associated participants' do expect(task.participating_users.count).to eq participants_count expect(Participant.count).to eq participants_count end end end end
En las siguientes clases, explicaremos a profundidad el flujo y la construcción de nuestras pruebas, haremos más de 30 pruebas al final de nuestro curso, así que no detallaré todos los procesos del contenido de este archivo, sin embargo, lo que estamos haciendo allí, es definir en el cuerpo de la prueba la construcción de una nueva tarea con 4 participantes, y estamos comprobando dos cosas: que esta construcción luego pueda ser persistida y que después de ser persistida el número de participantes persistidos si sea el número que inicialmente he definido, es decir 4.
Para ejecutar las pruebas, debemos ir a la consola de comandos, y en la raíz del proyecto ejecutar lo siguiente
$ rspec
Deberás ver el siguiente resultado como salida del comando
Task #save is persisted after save has all associated participants Finished in 0.2038 seconds (files took 0.77674 seconds to load) 2 examples, 0 failures