Search code examples
ruby-on-railsfactory-bot

FactoryBot: Trait not registered: "todo_id"


I've been working through a tutorial that introduced factory bot in order to help with the testing. The tutorial has been otherwise great, but I haven't been able to resolve a means to fixing this error:

Failure/Error: let!(:items) { create_list(:item, 20, todo_id: todo.id) }

      KeyError:
        Trait not registered: "todo_id"

items_spec.rb

RSpec.describe 'Items API' do
  # Initialize the test data
  let!(:todo) { create(:todo) }
  let!(:items) { create_list(:item, 20, todo_id: todo.id) }
  let(:todo_id) { todo.id }
  let(:id) { items.first.id }
...

FB items.rb

FactoryBot.define do
  factory :item do
    name { Faker::StarWars.character }
    done { false }
    todo_id nil
  end
end

FB todos.rb

FactoryBot.define do
  factory :todo do
    title { Faker::Lorem.word }
    created_by { Faker::Number.number(digits: 10) }
  end
end

routes.rb

Rails.application.routes.draw do
  resources :todos do
    resources :items
  end
end

Migrations

Todos have many Items

class CreateTodos < ActiveRecord::Migration[6.0]
  def change
    create_table :todos do |t|
      t.string :title
      t.string :created_by

      t.timestamps
    end
  end
end
class CreateItems < ActiveRecord::Migration[6.0]
  def change
    create_table :items do |t|
      t.string :name
      t.boolean :done
      t.references :todo, null: false, foreign_key: true

      t.timestamps
    end
  end
end

I imagine it's a relatively simple fix, but I am new to factory bots and I'm hesitant to follow some possible solutions because they change a lot of stuff. If anyone has a solution, i'd love to know otherwise I'm going to keep trying to dig around factorybot docs, which isnt the worst thing.


Solution

  • It's possible to set up associations within factories. If the factory name is the same as the association name, the factory name can be left out.
    - FactoryBot Readme

    FactoryBot.define do
      factory :item do
        name { Faker::Lorem.character }
        done { false }
        todo
      end
    end