thoughtbot/factory_bot · error · FactoryBot::MethodDefinitionError
Defining methods in blocks (trait or factory) is not support
Error message
Defining methods in blocks (trait or factory) is not supported (#{name}) What it means
MethodDefinitionError raised via DefinitionProxy#singleton_method_added (definition_proxy.rb:32-35). Factory, trait, and transient bodies are instance_eval'd against a DefinitionProxy from which almost all methods have been undefined; writing `def something` inside the block defines a singleton method on the proxy, which fires the hook and raises immediately. DSL blocks only accept attribute declarations, so helper methods are rejected.
Source
Thrown at lib/factory_bot/definition_proxy.rb:34
].freeze
(instance_methods + private_instance_methods).each do |method|
undef_method(method) unless UNPROXIED_METHODS.include?(method.to_s)
end
delegate :before, :after, :callback, to: :@definition
attr_reader :child_factories
def initialize(definition, ignore = false)
@definition = definition
@ignore = ignore
@child_factories = []
end
def singleton_method_added(name)
message = "Defining methods in blocks (trait or factory) is not supported (#{name})"
raise FactoryBot::MethodDefinitionError, message
end
# Adds an attribute to the factory.
# The attribute value will be generated "lazily"
# by calling the block whenever an instance is generated.
# The block will not be called if the
# attribute is overridden for a specific instance.
#
# Arguments:
# * name: +Symbol+ or +String+
# The name of this attribute. This will be assigned using "name=" for
# generated instances.
def add_attribute(name, &block)
declaration = Declaration::Dynamic.new(name, @ignore, block)
@definition.declare_attribute(declaration)
end
def transient(&block)View on GitHub (pinned to 18ae8b581b)
Solutions
- Inline the logic into the attribute block, or compute it in a transient attribute that other attributes reference.
- Put the helper on the model or in a plain module and call it from the blocks.
- For helpers needed inside dynamic blocks, include a module into FactoryBot::SyntaxRunner via an initializer (`FactoryBot::SyntaxRunner.include MyHelpers`).
- Use `initialize_with { ... }` to route construction through a builder that encapsulates the logic.
Example fix
# before
FactoryBot.define do
factory :user do
def email_domain; 'example.com'; end # raises MethodDefinitionError
email { "user@#{email_domain}" }
end
end
# after
FactoryBot.define do
factory :user do
email { 'user@example.com' } # inline it, or use a model/SyntaxRunner helper
end
end Defensive patterns
Strategy: validation
Try / catch
begin require 'spec/factories' rescue FactoryBot::MethodDefinitionError => e # e.message names the method; move the def to the model, a module, or FactoryBot::SyntaxRunner end
Prevention
- Never use `def` inside factory, trait, or transient blocks.
- Share helper logic via modules included on the model, or into FactoryBot::SyntaxRunner for use inside dynamic blocks.
- Use transient attributes to compute derived values instead of helper methods.
When it happens
Trigger: Inside `factory :user do ... end`, `trait :x do ... end`, or `transient do ... end`, write `def email_domain; 'example.com'; end`. The hook raises at definition (load) time: "Defining methods in blocks (trait or factory) is not supported (email_domain)".
Common situations: Trying to DRY logic shared by several attribute blocks; developers used to block DSLs (Rake, RSpec) where `def` inside a block works; porting object-mother or fixture code that contained helper methods.
Related errors
- Unexpected block passed to '#{name}' association in '#{@defi
- Association '#{name}' received an invalid factory argument.
- Association '#{name}' received an invalid attribute override
- undefined method '#{name}' in '#{@definition.name}' factory
- Attribute already defined: #{attribute.name}
AI-assisted analysis of thoughtbot/factory_bot@18ae8b581b (2026-08-21).
Data as JSON: /api/errors/75eced70f40784f6.
Report an issue: GitHub.