Creación de Fábricas Automatizadas con FactoryBot y Faker en Rails

Clase 8 de 34Curso 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