The project is in a healthy, maintained state
Lightweight DSL for building test data objects without ActiveRecord. Define factories with default attributes, apply traits for variations, and use thread-safe sequences for unique values.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

philiprehberger-test_factory

Tests Gem Version Last updated

Lightweight test data factory DSL with sequences and traits

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-test_factory"

Or install directly:

gem install philiprehberger-test_factory

Usage

require "philiprehberger/test_factory"

# Define a factory
Philiprehberger::TestFactory.define(:user) do
  { name: "Alice", email: "alice@example.com", role: "user" }
end

# Build a single object
user = Philiprehberger::TestFactory.build(:user)
# => { name: "Alice", email: "alice@example.com", role: "user" }

# Build with overrides
admin = Philiprehberger::TestFactory.build(:user, role: "admin")
# => { name: "Alice", email: "alice@example.com", role: "admin" }

Traits

Philiprehberger::TestFactory.trait(:user, :admin) { { role: "admin" } }
Philiprehberger::TestFactory.trait(:user, :inactive) { { active: false } }

admin = Philiprehberger::TestFactory.build(:user, traits: [:admin])
# => { name: "Alice", email: "alice@example.com", role: "admin" }

Sequences

Philiprehberger::TestFactory.sequence(:email) { |n| "user_#{n}@example.com" }

# Access via the registry
email = Philiprehberger::TestFactory.send(:registry).next_in_sequence(:email)
# => "user_1@example.com"

Build Lists

users = Philiprehberger::TestFactory.build_list(:user, 5)
# => Array of 5 user hashes

Callbacks

Philiprehberger::TestFactory.define(:user) do |f|
  f.after_build { |obj| obj[:created_at] = Time.now }
  { name: "Alice", email: "alice@example.com" }
end

Transient Attributes

Philiprehberger::TestFactory.define(:user) do |f|
  f.transient { admin false }
  f.after_build { |obj, transients| obj[:role] = "admin" if transients[:admin] }
  { name: "Alice", role: "user" }
end

user = Philiprehberger::TestFactory.build(:user, admin: true)
# => { name: "Alice", role: "admin" }

Associations

Philiprehberger::TestFactory.define(:user) { { name: "Alice" } }
Philiprehberger::TestFactory.define(:post) do |f|
  f.association :author, factory: :user
  { title: "Hello World" }
end

post = Philiprehberger::TestFactory.build(:post)
# => { title: "Hello World", author: { name: "Alice" } }

Reset

Philiprehberger::TestFactory.reset!

API

Method Description
TestFactory.define(name, &block) Register a factory; block returns a hash of defaults
TestFactory.trait(factory_name, trait_name, &block) Register a trait override for a factory
TestFactory.sequence(name, &block) Register a thread-safe auto-incrementing sequence
TestFactory.build(name, traits:, **overrides) Build a single data hash
TestFactory.build_list(name, count, traits:, **overrides) Build N data hashes
TestFactory.reset! Clear all definitions, traits, and sequences
DefinitionProxy#after_build(&block) Register a callback that runs after building
DefinitionProxy#transient(&block) Declare transient attributes excluded from the result
DefinitionProxy#association(name, factory:) Declare an association to another factory

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT