pax_global_header00006660000000000000000000000064150767651730014533gustar00rootroot0000000000000052 comment=a9884ff701872c2b8f1148cc8a34acac7694996b factory_bot-6.5.6/000077500000000000000000000000001507676517300140645ustar00rootroot00000000000000factory_bot-6.5.6/.github/000077500000000000000000000000001507676517300154245ustar00rootroot00000000000000factory_bot-6.5.6/.github/ISSUE_TEMPLATE/000077500000000000000000000000001507676517300176075ustar00rootroot00000000000000factory_bot-6.5.6/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000017351507676517300223070ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: 'bug' assignees: '' --- ### Description ### Reproduction Steps ### Expected behavior ### Actual behavior ### System configuration **factory_bot version**: **rails version**: **ruby version**: factory_bot-6.5.6/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000012431507676517300233340ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: 'feature' assignees: '' --- ### Problem this feature will solve ### Desired solution ## Alternatives considered ## Additional context factory_bot-6.5.6/.github/REPRODUCTION_SCRIPT.rb000066400000000000000000000023321507676517300207520ustar00rootroot00000000000000require "bundler/inline" gemfile(true) do source "https://rubygems.org" git_source(:github) { |repo| "https://github.com/#{repo}.git" } gem "factory_bot", "~> 6.0" gem "activerecord" gem "sqlite3" end require "active_record" require "factory_bot" require "minitest/autorun" require "logger" ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do # TODO: Update the schema to include the specific tables or columns necessary # to reproduct the bug create_table :posts, force: true do |t| t.string :body end end # TODO: Add any application specific code necessary to reproduce the bug class Post < ActiveRecord::Base end FactoryBot.define do # TODO: Write the factory definitions necessary to reproduce the bug factory :post do body { "Post body" } end end class FactoryBotTest < Minitest::Test def test_factory_bot_stuff # TODO: Write a failing test case to demonstrate what isn't working as # expected body_override = "Body override" post = FactoryBot.build(:post, body: body_override) assert_equal post.body, body_override end end # Run the tests with `ruby ` factory_bot-6.5.6/.github/dependabot.yml000066400000000000000000000004561507676517300202610ustar00rootroot00000000000000--- version: 2 updates: - package-ecosystem: "bundler" directory: "/" schedule: interval: weekly time: "02:00" timezone: "Etc/UTC" - package-ecosystem: "github-actions" directory: "/" schedule: interval: weekly time: "02:00" timezone: "Etc/UTC" factory_bot-6.5.6/.github/workflows/000077500000000000000000000000001507676517300174615ustar00rootroot00000000000000factory_bot-6.5.6/.github/workflows/build.yml000066400000000000000000000032721507676517300213070ustar00rootroot00000000000000name: Build on: pull_request: push: branches: [ main ] jobs: build: name: Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} strategy: fail-fast: false matrix: ruby: - jruby-9.4 - truffleruby - "3.4" - "3.3" - "3.2" - "3.1" - "3.0" rails: - "6.1" - "7.0" - "7.1" - "7.2" - main exclude: - { ruby: jruby-9.4, rails: "7.1" } - { ruby: jruby-9.4, rails: "7.2" } - { ruby: jruby-9.4, rails: main } # Rails >= 7.2 requires Ruby 3.1 - { ruby: "3.0", rails: "7.2" } - { ruby: "3.0", rails: "main" } # Rails >= 8.0 requires Ruby 3.2 - { ruby: "3.1", rails: "main" } # 2025-10-23 There's an incompatibility between truffleruby and rails edge Git ref # results in a "NoMethodError: undefined method `[]' for class Fiber" error whilst running specs - { ruby: truffleruby, rails: "main" } runs-on: 'ubuntu-latest' env: BUNDLE_GEMFILE: gemfiles/${{ matrix.rails }}.gemfile steps: - uses: actions/checkout@v5 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - name: Setup project run: bundle install - name: Run test run: bundle exec rake all_specs standard: name: Run standard runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v5 - uses: ruby/setup-ruby@v1 with: ruby-version: "3.3" - name: Setup project run: bundle install - name: Run test run: bundle exec rake standard factory_bot-6.5.6/.github/workflows/docs.yml000066400000000000000000000020131507676517300211300ustar00rootroot00000000000000name: Docs on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest permissions: contents: write # To push a branch pull-requests: write # To create a PR from that branch steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - name: Install mdbook run: | mkdir mdbook curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.27/mdbook-v0.4.27-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook echo `pwd`/mdbook >> $GITHUB_PATH - name: Deploy GitHub Pages run: | cd docs mdbook build git worktree add gh-pages git config user.name "Deploy from CI" git config user.email "" cd gh-pages # Delete the ref to avoid keeping history. git update-ref -d refs/heads/gh-pages rm -rf * mv ../book/* . git add . git commit -m "Deploy $GITHUB_SHA to gh-pages" git push --force --set-upstream origin gh-pages factory_bot-6.5.6/.github/workflows/dynamic-readme.yml000066400000000000000000000005411507676517300230630ustar00rootroot00000000000000name: update-templates on: push: branches: - main paths: - README.md workflow_dispatch: jobs: update-templates: permissions: contents: write pull-requests: write pages: write uses: thoughtbot/templates/.github/workflows/dynamic-readme.yaml@main secrets: token: ${{ secrets.GITHUB_TOKEN }} factory_bot-6.5.6/.github/workflows/dynamic-security.yml000066400000000000000000000005431507676517300234770ustar00rootroot00000000000000name: update-security on: push: paths: - SECURITY.md branches: - main workflow_dispatch: jobs: update-security: permissions: contents: write pull-requests: write pages: write uses: thoughtbot/templates/.github/workflows/dynamic-security.yaml@main secrets: token: ${{ secrets.GITHUB_TOKEN }} factory_bot-6.5.6/.gitignore000066400000000000000000000003351507676517300160550ustar00rootroot00000000000000.bundle .rubocop-https* .yardoc .zed *.swp bin coverage test.db tmp # yard doc # gem builds factory_bot-*.gem factory_girl-*.gem # mdBook generated output docs/book # lock files generated by appraisal gemfiles/*.lock factory_bot-6.5.6/.irbrc000066400000000000000000000005601507676517300151670ustar00rootroot00000000000000def reload! # Undefine FactoryBot so we can reload constants and fresh code Object.send(:remove_const, :FactoryBot) if Object.const_defined?(:FactoryBot) # Remove all files from the 'loaded' register $LOADED_FEATURES.delete_if { |path| path.match?(/factory_bot/) } # re-load it again require "factory_bot" puts "\nfactory_bot reloaded!\n\n" true end factory_bot-6.5.6/.rspec000066400000000000000000000000601507676517300151750ustar00rootroot00000000000000--format progress --color --require spec_helper factory_bot-6.5.6/.simplecov000066400000000000000000000001021507676517300160570ustar00rootroot00000000000000SimpleCov.start do add_filter "/spec/" add_filter "/tmp/" end factory_bot-6.5.6/.standard.yml000066400000000000000000000000641507676517300164650ustar00rootroot00000000000000ruby_version: "3.0" parallel: true format: progress factory_bot-6.5.6/.standard_todo.yml000066400000000000000000000002441507676517300175120ustar00rootroot00000000000000# Auto generated files with errors to ignore. # Remove from this list as you refactor files. --- ignore: - lib/factory_bot/linter.rb: - Lint/SharedMutableDefault factory_bot-6.5.6/.yardopts000066400000000000000000000001011507676517300157220ustar00rootroot00000000000000lib/**/*.rb - GETTING_STARTED.md CONTRIBUTING.md NAME.md LICENSE factory_bot-6.5.6/Appraisals000066400000000000000000000022461507676517300161120ustar00rootroot00000000000000appraise "6.1" do gem "activerecord", "~> 6.1.0" gem "activerecord-jdbcsqlite3-adapter", "~> 61.0", platforms: [:jruby] gem "sqlite3", "~> 1.4", platforms: [:ruby] gem "concurrent-ruby", "< 1.3.5" end appraise "7.0" do gem "activerecord", "~> 7.0.0" gem "activerecord-jdbcsqlite3-adapter", "~> 70.0", platforms: [:jruby] gem "sqlite3", "~> 1.4", platforms: [:ruby] gem "concurrent-ruby", "< 1.3.5" end appraise "7.1" do gem "activerecord", "~> 7.1.0" # When version 71 is released, uncomment this and also allow it in the GitHub # Action build workflow. # gem "activerecord-jdbcsqlite3-adapter", "~> 71.0", platforms: [:jruby] gem "sqlite3", "~> 1.4", platforms: [:ruby] end appraise "7.2" do gem "activerecord", "~> 7.2.0" # When version 71 is released, uncomment this and also allow it in the GitHub # Action build workflow. # gem "activerecord-jdbcsqlite3-adapter", "~> 71.0", platforms: [:jruby] gem "sqlite3", platforms: [:ruby] end appraise "main" do gem "activerecord", git: "https://github.com/rails/rails.git", branch: "main" gem "activerecord-jdbcsqlite3-adapter", "~> 70.0", platforms: [:jruby] gem "sqlite3", platforms: [:ruby] end factory_bot-6.5.6/CODEOWNERS000066400000000000000000000010351507676517300154560ustar00rootroot00000000000000# Lines starting with '#' are comments. # Each line is a file pattern followed by one or more owners. # More details are here: https://help.github.com/articles/about-codeowners/ # The '*' pattern is global owners. # Order is important. The last matching pattern has the most precedence. # The folders are ordered as follows: # In each subsection folders are ordered first by depth, then alphabetically. # This should make it easy to add new rules without breaking existing ones. # Global rule: * @neilvcarvalho @doodlingdev @vburzynski factory_bot-6.5.6/CODE_OF_CONDUCT.md000066400000000000000000000002511507676517300166610ustar00rootroot00000000000000# Code of conduct By participating in this project, you agree to abide by the [thoughtbot code of conduct][1]. [1]: https://thoughtbot.com/open-source-code-of-conduct factory_bot-6.5.6/CONTRIBUTING.md000066400000000000000000000061501507676517300163170ustar00rootroot00000000000000# Contributing to Factory Bot We love pull requests from everyone. By participating in this project, you agree to abide by the thoughtbot [code of conduct]. [code of conduct]: https://thoughtbot.com/open-source-code-of-conduct Here are some ways *you* can contribute: * by using alpha, beta, and prerelease versions * by reporting bugs * by suggesting new features * by writing or editing documentation * by writing specifications * by writing code ( **no patch is too small** : fix typos, add comments, etc. ) * by refactoring code * by closing [issues][] * by reviewing patches [issues]: https://github.com/thoughtbot/factory_bot/issues ## Submitting an Issue * We use the [GitHub issue tracker][issues] to track bugs and features. * Before submitting a bug report or feature request, check to make sure it hasn't already been submitted. * When submitting a bug report, please include a [reproduction script] and any other details that may be necessary to reproduce the bug, including your gem version, Ruby version, and operating system. ## Cleaning up issues * Issues that have no response from the submitter will be closed after 30 days. * Issues will be closed once they're assumed to be fixed or answered. If the maintainer is wrong, it can be opened again. * If your issue is closed by mistake, please understand and explain the issue. We will happily reopen the issue. ## Submitting a Pull Request 1. [Fork][fork] the [official repository][repo]. 1. [Create a topic branch.][branch] 1. Implement your feature or bug fix. 1. Add, commit, and push your changes. 1. [Submit a pull request.][pr] ### Notes * Please add tests if you changed code. Contributions without tests won't be accepted. * If you don't know how to add tests, please put in a PR and leave a comment asking for help. We love helping! * Please don't update the Gem version. ## Setting up ```sh bundle install ``` ## Running the test suite The default rake task will run the full test suite and [standard]: ```sh bundle exec rake ``` You can also run a single group of tests (unit, spec, or feature) ```sh bundle exec rake spec:unit bundle exec rake spec:acceptance bundle exec rake features ``` To run an individual rspec test, you can provide a path and line number: ```sh bundle exec rspec spec/path/to/spec.rb:123 ``` You can run tests with a specific version of rails via [appraisal]. To run the default rake task against Rails 6, for example: ```sh bundle exec appraisal 6.0 rake ``` ## Formatting Use [standard] to automatically format your code: ```sh bundle exec rake standard:fix ``` [repo]: https://github.com/thoughtbot/factory_bot/tree/main [fork]: https://help.github.com/articles/fork-a-repo/ [branch]: https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/ [pr]: https://help.github.com/articles/using-pull-requests/ [standard]: https://github.com/testdouble/standard [appraisal]: https://github.com/thoughtbot/appraisal [reproduction script]: https://github.com/thoughtbot/factory_bot/blob/main/.github/REPRODUCTION_SCRIPT.rb Inspired by https://github.com/middleman/middleman-heroku/blob/master/CONTRIBUTING.md factory_bot-6.5.6/GETTING_STARTED.md000066400000000000000000001455531507676517300167120ustar00rootroot00000000000000**Deprecated** See our extensive reference, guides, and cookbook in [the factory_bot book][]. For information on integrations with third party libraries, such as RSpec or Rails, see [the factory_bot wiki][]. We also have [a detailed introductory video][], available for free on Upcase. [a detailed introductory video]: https://upcase.com/videos/factory-bot?utm_source=github&utm_medium=open-source&utm_campaign=factory-girl [the factory_bot book]: https://thoughtbot.github.io/factory_bot [the factory_bot wiki]: https://github.com/thoughtbot/factory_bot/wiki This document is deprecated and preserved for historical use. It may disappear at any time. Getting Started =============== - [Setup](#setup) - [Update Your Gemfile](#update-your-gemfile) - [Configure your test suite](#configure-your-test-suite) - [RSpec](#rspec) - [Test::Unit](#testunit) - [Cucumber](#cucumber) - [Spinach](#spinach) - [Minitest](#minitest) - [Minitest::Spec](#minitestspec) - [minitest-rails](#minitest-rails) - [Defining factories](#defining-factories) - [Factory name and attributes](#factory-name-and-attributes) - [Specifying the class explicitly](#specifying-the-class-explicitly) - [Hash attributes](#hash-attributes) - [Best practices](#best-practices) - [Definition file paths](#definition-file-paths) - [Static Attributes](#static-attributes) - [Using factories](#using-factories) - [Build strategies](#build-strategies) - [Attribute overrides](#attribute-overrides) - [build\_stubbed and Marshal.dump](#build_stubbed-and-marshaldump) - [Aliases](#aliases) - [Dependent Attributes](#dependent-attributes) - [Transient Attributes](#transient-attributes) - [With other attributes](#with-other-attributes) - [With attributes\_for](#with-attributes_for) - [With callbacks](#with-callbacks) - [With associations](#with-associations) - [Method Name / Reserved Word Attributes](#method-name--reserved-word-attributes) - [Inheritance](#inheritance) - [Nested factories](#nested-factories) - [Assigning parent explicitly](#assigning-parent-explicitly) - [Best practices](#best-practices-1) - [Associations](#associations) - [Implicit definition](#implicit-definition) - [Explicit definition](#explicit-definition) - [Inline definition](#inline-definition) - [Specifying the factory](#specifying-the-factory) - [Overriding attributes](#overriding-attributes) - [Association overrides](#association-overrides) - [Build strategies](#build-strategies-1) - [has\_many associations](#has_many-associations) - [has\_and\_belongs\_to\_many associations](#has_and_belongs_to_many-associations) - [Polymorphic associations](#polymorphic-associations) - [Interconnected associations](#interconnected-associations) - [Sequences](#sequences) - [Global sequences](#global-sequences) - [With dynamic attributes](#with-dynamic-attributes) - [As implicit attributes](#as-implicit-attributes) - [Inline sequences](#inline-sequences) - [Initial value](#initial-value) - [Without a block](#without-a-block) - [Aliases](#aliases-1) - [Rewinding](#rewinding) - [Uniqueness](#uniqueness) - [Traits](#traits) - [Defining traits](#defining-traits) - [As implicit attributes](#as-implicit-attributes-1) - [Attribute precedence](#attribute-precedence) - [In child factories](#in-child-factories) - [As mixins](#as-mixins) - [Using traits](#using-traits) - [With associations](#with-associations-1) - [Traits within traits](#traits-within-traits) - [With transient attributes](#with-transient-attributes) - [Enum traits](#enum-traits) - [Callbacks](#callbacks) - [Default callbacks](#default-callbacks) - [Multiple callbacks](#multiple-callbacks) - [Global callbacks](#global-callbacks) - [Symbol#to\_proc](#symbolto_proc) - [Modifying factories](#modifying-factories) - [Building or Creating Multiple Records](#building-or-creating-multiple-records) - [Linting Factories](#linting-factories) - [Custom Construction](#custom-construction) - [Custom Strategies](#custom-strategies) - [Custom Callbacks](#custom-callbacks) - [Custom Methods to Persist Objects](#custom-methods-to-persist-objects) - [ActiveSupport Instrumentation](#activesupport-instrumentation) - [Rails Preloaders and RSpec](#rails-preloaders-and-rspec) - [Using Without Bundler](#using-without-bundler) Setup ----- ### Update Your Gemfile If you're using Rails: ```ruby gem "factory_bot_rails" ``` If you're *not* using Rails: ```ruby gem "factory_bot" ``` ### Configure your test suite #### RSpec If you're using Rails, add the following configuration to `spec/support/factory_bot.rb` and be sure to require that file in `rails_helper.rb`: ```ruby RSpec.configure do |config| config.include FactoryBot::Syntax::Methods end ``` If you're *not* using Rails: ```ruby RSpec.configure do |config| config.include FactoryBot::Syntax::Methods config.before(:suite) do FactoryBot.find_definitions end end ``` #### Test::Unit ```ruby class Test::Unit::TestCase include FactoryBot::Syntax::Methods end ``` #### Cucumber ```ruby # env.rb (Rails example location - RAILS_ROOT/features/support/env.rb) World(FactoryBot::Syntax::Methods) ``` #### Spinach ```ruby class Spinach::FeatureSteps include FactoryBot::Syntax::Methods end ``` #### Minitest ```ruby class Minitest::Unit::TestCase include FactoryBot::Syntax::Methods end ``` #### Minitest::Spec ```ruby class Minitest::Spec include FactoryBot::Syntax::Methods end ``` #### minitest-rails ```ruby class ActiveSupport::TestCase include FactoryBot::Syntax::Methods end ``` If you do not include `FactoryBot::Syntax::Methods` in your test suite, then all factory\_bot methods will need to be prefaced with `FactoryBot`. Defining factories ------------------ ### Factory name and attributes Each factory has a name and a set of attributes. The name is used to guess the class of the object by default: ```ruby # This will guess the User class FactoryBot.define do factory :user do first_name { "John" } last_name { "Doe" } admin { false } end end ``` ### Specifying the class explicitly It is also possible to explicitly specify the class: ```ruby # This will use the User class (otherwise Admin would have been guessed) factory :admin, class: "User" ``` Explicit specification of the class, _with the full namespace_, is necessary when defining factories for classes nested within other modules or classes: ```ruby # foo/bar.rb module Foo class Bar ... end end # factories.rb FactoryBot.define do factory :bar, class: 'Foo::Bar' do ... end end ``` If the full namespace is not provided in the `factory` statement, you will receive a `NameError: uninitialized constant Bar` error. You can pass a constant as well, if the constant is available (note that this can cause test performance problems in large Rails applications, since referring to the constant will cause it to be eagerly loaded). ```ruby factory :access_token, class: User ``` ### Hash attributes Because of the block syntax in Ruby, defining attributes as `Hash`es (for serialized/JSON columns, for example) requires two sets of curly brackets: ```ruby factory :program do configuration { { auto_resolve: false, auto_define: true } } end ``` ### Best practices It is recommended that you have one factory for each class that provides the simplest set of attributes necessary to create an instance of that class. If you're creating ActiveRecord objects, that means that you should only provide attributes that are required through validations and that do not have defaults. Other factories can be created through inheritance to cover common scenarios for each class. Attempting to define multiple factories with the same name will raise an error. ### Definition file paths Factories can be defined anywhere, but will be automatically loaded after calling `FactoryBot.find_definitions` if factories are defined in files at the following locations: factories.rb factories/**/*.rb test/factories.rb test/factories/**/*.rb spec/factories.rb spec/factories/**/*.rb ### Static Attributes Static attributes (without a block) are no longer available in factory\_bot 5. You can read more about the decision to remove them in [this blog post](https://robots.thoughtbot.com/deprecating-static-attributes-in-factory_bot-4-11). Using factories --------------- ### Build strategies factory\_bot supports several different build strategies: build, create, attributes\_for and build\_stubbed: ```ruby # Returns a User instance that's not saved user = build(:user) # Returns a saved User instance user = create(:user) # Returns a hash of attributes that can be used to build a User instance attrs = attributes_for(:user) # Integrates with Ruby 3.0's support for pattern matching assignment attributes_for(:user) => {email:, name:, **attrs} # Returns an object with all defined attributes stubbed out stub = build_stubbed(:user) # Passing a block to any of the methods above will yield the return object create(:user) do |user| user.posts.create(attributes_for(:post)) end ``` ### Attribute overrides No matter which strategy is used, it's possible to override the defined attributes by passing a Hash: ```ruby # Build a User instance and override the first_name property user = build(:user, first_name: "Joe") user.first_name # => "Joe" ``` Overriding associations is also supported: ```ruby account = build(:account, :deluxe) friends = build_list(:user, 2) user = build(:user, account: account, friends: friends) ``` Ruby 3.1's support for [omitting values][] from `Hash` literals dovetails with attribute overrides and provides an opportunity to limit the repetition of variable names: ```ruby account = build(:account, :deluxe) friends = build_list(:user, 2) # The keyword arguments correspond to local variable names, so omit their values user = build(:user, account:, friends:) ``` [omitting values]: https://docs.ruby-lang.org/en/3.1/syntax/literals_rdoc.html#label-Hash+Literals ### build_stubbed and Marshal.dump Note that objects created with `build_stubbed` cannot be serialized with `Marshal.dump`, since factory\_bot defines singleton methods on these objects. Aliases ------- factory\_bot allows you to define aliases to existing factories to make them easier to re-use. This could come in handy when, for example, your Post object has an author attribute that actually refers to an instance of a User class. While normally factory\_bot can infer the factory name from the association name, in this case it will look for an author factory in vain. So, alias your user factory so it can be used under alias names. ```ruby factory :user, aliases: [:author, :commenter] do first_name { "John" } last_name { "Doe" } date_of_birth { 18.years.ago } end factory :post do # The alias allows us to write author instead of # association :author, factory: :user author title { "How to read a book effectively" } body { "There are five steps involved." } end factory :comment do # The alias allows us to write commenter instead of # association :commenter, factory: :user commenter body { "Great article!" } end ``` Dependent Attributes -------------------- Attributes can be based on the values of other attributes using the evaluator that is yielded to dynamic attribute blocks: ```ruby factory :user do first_name { "Joe" } last_name { "Blow" } email { "#{first_name}.#{last_name}@example.com".downcase } end create(:user, last_name: "Doe").email # => "joe.doe@example.com" ``` Transient Attributes -------------------- Transient attributes are attributes only available within the factory definition, and not set on the object being built. This allows for more complex logic inside factories. ### With other attributes There may be times where your code can be DRYed up by passing in transient attributes to factories. You can access transient attributes within other attributes (see [Dependent Attributes](#dependent-attributes)): ```ruby factory :user do transient do rockstar { true } end name { "John Doe#{" - Rockstar" if rockstar}" } end create(:user).name #=> "John Doe - ROCKSTAR" create(:user, rockstar: false).name #=> "John Doe" ``` ### With attributes_for Transient attributes will be ignored within attributes\_for and won't be set on the model, even if the attribute exists or you attempt to override it. ### With callbacks If you need to access the evaluator in a factory\_bot callback, you'll need to declare a second block argument (for the evaluator) and access transient attributes from there. ```ruby factory :user do transient do upcased { false } end name { "John Doe" } after(:create) do |user, evaluator| user.name.upcase! if evaluator.upcased end end create(:user).name #=> "John Doe" create(:user, upcased: true).name #=> "JOHN DOE" ``` ### With associations Transient [associations](#associations) are not supported in factory\_bot. Associations within the transient block will be treated as regular, non-transient associations. If needed, you can generally work around this by building a factory within a transient attribute: ```ruby factory :post factory :user do transient do post { build(:post) } end end ``` Method Name / Reserved Word Attributes ------------------------------- If your attributes conflict with existing methods or reserved words (all methods in the [DefinitionProxy](https://github.com/thoughtbot/factory_bot/blob/main/lib/factory_bot/definition_proxy.rb) class) you can define them with `add_attribute`. ```ruby factory :dna do add_attribute(:sequence) { 'GATTACA' } end factory :payment do add_attribute(:method) { 'paypal' } end ``` Inheritance ----------- ### Nested factories You can easily create multiple factories for the same class without repeating common attributes by nesting factories: ```ruby factory :post do title { "A title" } factory :approved_post do approved { true } end end approved_post = create(:approved_post) approved_post.title # => "A title" approved_post.approved # => true ``` ### Assigning parent explicitly You can also assign the parent explicitly: ```ruby factory :post do title { "A title" } end factory :approved_post, parent: :post do approved { true } end ``` ### Best practices As mentioned above, it's good practice to define a basic factory for each class with only the attributes required to create it. Then, create more specific factories that inherit from this basic parent. Factory definitions are still code, so keep them DRY. Associations ------------ ### Implicit definition 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. ```ruby factory :post do # ... author end ``` ### Explicit definition You can define associations explicitly. This can be handy especially when [Overriding attributes](#overriding-attributes) ```ruby factory :post do # ... association :author end ``` ### Inline definition You can also define associations inline within regular attributes, but note that the value will be `nil` when using the `attributes_for` strategy. ```ruby factory :post do # ... author { association :author } end ``` ### Specifying the factory You can specify a different factory (although [Aliases](#aliases) might also help you out here). Implicitly: ```ruby factory :post do # ... author factory: :user end ``` Explicitly: ```ruby factory :post do # ... association :author, factory: :user end ``` Inline: ```ruby factory :post do # ... author { association :user } end ``` ### Overriding attributes You can also override attributes. Implicitly: ```ruby factory :post do # ... author factory: :author, last_name: "Writely" end ``` Explicitly: ```ruby factory :post do # ... association :author, last_name: "Writely" end ``` Or inline using attributes from the factory: ```ruby factory :post do # ... author_last_name { "Writely" } author { association :author, last_name: author_last_name } end ``` ### Association overrides Attribute overrides can be used to link associated objects: ```ruby FactoryBot.define do factory :author do name { 'Taylor' } end factory :post do author end end eunji = build(:author, name: 'Eunji') post = build(:post, author: eunji) ``` ### Build strategies In factory\_bot 5, associations default to using the same build strategy as their parent object: ```ruby FactoryBot.define do factory :author factory :post do author end end post = build(:post) post.new_record? # => true post.author.new_record? # => true post = create(:post) post.new_record? # => false post.author.new_record? # => false ``` This is different than the default behavior for previous versions of factory\_bot, where the association strategy would not always match the strategy of the parent object. If you want to continue using the old behavior, you can set the `use_parent_strategy` configuration option to `false`. ```ruby FactoryBot.use_parent_strategy = false # Builds and saves a User and a Post post = create(:post) post.new_record? # => false post.author.new_record? # => false # Builds and saves a User, and then builds but does not save a Post post = build(:post) post.new_record? # => true post.author.new_record? # => false ``` To not save the associated object, specify `strategy: :build` in the factory: ```ruby FactoryBot.use_parent_strategy = false factory :post do # ... association :author, factory: :user, strategy: :build end # Builds a User, and then builds a Post, but does not save either post = build(:post) post.new_record? # => true post.author.new_record? # => true ``` Please note that the `strategy: :build` option must be passed to an explicit call to `association`, and cannot be used with implicit associations: ```ruby factory :post do # ... author strategy: :build # <<< this does *not* work; causes author_id to be nil ``` ### has_many associations There are a few ways to generate data for a `has_many` relationship. The simplest approach is to write a helper method in plain Ruby to tie together the different records: ```ruby FactoryBot.define do factory :post do title { "Through the Looking Glass" } user end factory :user do name { "Rachel Sanchez" } end end def user_with_posts(posts_count: 5) FactoryBot.create(:user) do |user| FactoryBot.create_list(:post, posts_count, user: user) end end create(:user).posts.length # 0 user_with_posts.posts.length # 5 user_with_posts(posts_count: 15).posts.length # 15 ``` If you prefer to keep the object creation fully within factory\_bot, you can build the posts in an `after(:create)` callback. ```ruby FactoryBot.define do factory :post do title { "Through the Looking Glass" } user end factory :user do name { "John Doe" } # user_with_posts will create post data after the user has been created factory :user_with_posts do # posts_count is declared as a transient attribute available in the # callback via the evaluator transient do posts_count { 5 } end # the after(:create) yields two values; the user instance itself and the # evaluator, which stores all values from the factory, including transient # attributes; `create_list`'s second argument is the number of records # to create and we make sure the user is associated properly to the post after(:create) do |user, evaluator| create_list(:post, evaluator.posts_count, user: user) # You may need to reload the record here, depending on your application user.reload end end end end create(:user).posts.length # 0 create(:user_with_posts).posts.length # 5 create(:user_with_posts, posts_count: 15).posts.length # 15 ``` Or, for a solution that works with `build`, `build_stubbed`, and `create` (although it doesn't work well with `attributes_for`), you can use inline associations: ```ruby FactoryBot.define do factory :post do title { "Through the Looking Glass" } user end factory :user do name { "Taylor Kim" } factory :user_with_posts do posts { [association(:post)] } end end end create(:user).posts.length # 0 create(:user_with_posts).posts.length # 1 build(:user_with_posts).posts.length # 1 build_stubbed(:user_with_posts).posts.length # 1 ``` For more flexibility you can combine this with the `posts_count` transient attribute from the callback example: ```ruby FactoryBot.define do factory :post do title { "Through the Looking Glass" } user end factory :user do name { "Adiza Kumato" } factory :user_with_posts do transient do posts_count { 5 } end posts do Array.new(posts_count) { association(:post) } end end end end create(:user_with_posts).posts.length # 5 create(:user_with_posts, posts_count: 15).posts.length # 15 build(:user_with_posts, posts_count: 15).posts.length # 15 build_stubbed(:user_with_posts, posts_count: 15).posts.length # 15 ``` ### has_and_belongs_to_many associations Generating data for a `has_and_belongs_to_many` relationship is very similar to the above `has_many` relationship, with a small change: you need to pass an array of objects to the model's pluralized attribute name rather than a single object to the singular version of the attribute name. ```ruby def profile_with_languages(languages_count: 2) FactoryBot.create(:profile) do |profile| FactoryBot.create_list(:language, languages_count, profiles: [profile]) end end ``` Or with the callback approach: ```ruby factory :profile_with_languages do transient do languages_count { 2 } end after(:create) do |profile, evaluator| create_list(:language, evaluator.languages_count, profiles: [profile]) profile.reload end end ``` Or the inline association approach (note the use of the `instance` method here to refer to the profile being built): ```ruby factory :profile_with_languages do transient do languages_count { 2 } end languages do Array.new(languages_count) do association(:language, profiles: [instance]) end end end ``` ### Polymorphic associations Polymorphic associations can be handled with traits: ```ruby FactoryBot.define do factory :video factory :photo factory :comment do for_photo # default to the :for_photo trait if none is specified trait :for_video do association :commentable, factory: :video end trait :for_photo do association :commentable, factory: :photo end end end ``` This allows us to do: ```ruby create(:comment) create(:comment, :for_video) create(:comment, :for_photo) ``` ### Interconnected associations There are limitless ways objects might be interconnected, and factory\_bot may not always be suited to handle those relationships. In some cases it makes sense to use factory\_bot to build each individual object, and then to write helper methods in plain Ruby to tie those objects together. That said, some more complex, interconnected relationships can be built in factory\_bot using inline associations with reference to the `instance` being built. Let's say your models look like this, where an associated `Student` and `Profile` should both belong to the same `School`: ```ruby class Student < ApplicationRecord belongs_to :school has_one :profile end class Profile < ApplicationRecord belongs_to :school belongs_to :student end class School < ApplicationRecord has_many :students has_many :profiles end ``` We can ensure the student and profile are connected to each other and to the same school with a factory like this: ```ruby FactoryBot.define do factory :student do school profile { association :profile, student: instance, school: school } end factory :profile do school student { association :student, profile: instance, school: school } end factory :school end ``` Note that this approach works with `build`, `build_stubbed`, and `create`, but the associations will return `nil` when using `attributes_for`. Also, note that if you assign any attributes inside a custom `initialize_with` (e.g. `initialize_with { new(**attributes) }`), those attributes should not refer to `instance`, since it will be `nil`. Sequences --------- ### Global sequences Unique values in a specific format (for example, e-mail addresses) can be generated using sequences. Sequences are defined by calling `sequence` in a definition block, and values in a sequence are generated by calling `generate`: ```ruby # Defines a new sequence FactoryBot.define do sequence :email do |n| "person#{n}@example.com" end end generate :email # => "person1@example.com" generate :email # => "person2@example.com" ``` ### With dynamic attributes Sequences can be used in dynamic attributes: ```ruby factory :invite do invitee { generate(:email) } end ``` ### As implicit attributes Or as implicit attributes: ```ruby factory :user do email # Same as `email { generate(:email) }` end ``` Note that defining sequences as implicit attributes will not work if you have a factory with the same name as the sequence. ### Inline sequences And it's also possible to define an in-line sequence that is only used in a particular factory: ```ruby factory :user do sequence(:email) { |n| "person#{n}@example.com" } end ``` With Ruby 2.7's support for [numbered parameters][], inline definitions can be even more abbreviated: ```ruby factory :user do sequence(:email) { "person#{_1}@example.com" } end ``` [numbered parameters]: https://ruby-doc.org/core-2.7.1/Proc.html#class-Proc-label-Numbered+parameters ### Initial value You can override the initial value. Any value that responds to the `#next` method will work (e.g. 1, 2, 3, 'a', 'b', 'c') ```ruby factory :user do sequence(:email, 1000) { |n| "person#{n}@example.com" } end ``` ### Without a block Without a block, the value will increment itself, starting at its initial value: ```ruby factory :post do sequence(:position) end ``` Please note, that the value for the sequence could be any Enumerable instance, as long as it responds to `#next`: ```ruby factory :task do sequence :priority, %i[low medium high urgent].cycle end ``` ### Aliases Sequences can also have aliases. The sequence aliases share the same counter: ```ruby factory :user do sequence(:email, 1000, aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" } end # will increase value counter for :email which is shared by :sender and :receiver generate(:sender) ``` Define aliases and use default value (1) for the counter ```ruby factory :user do sequence(:email, aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" } end ``` Setting the value: ```ruby factory :user do sequence(:email, 'a', aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" } end ``` The value just needs to support the `#next` method. Here the next value will be 'a', then 'b', etc. ### Rewinding Sequences can also be rewound with `FactoryBot.rewind_sequences`: ```ruby sequence(:email) {|n| "person#{n}@example.com" } generate(:email) # "person1@example.com" generate(:email) # "person2@example.com" generate(:email) # "person3@example.com" FactoryBot.rewind_sequences generate(:email) # "person1@example.com" ``` This rewinds all registered sequences. ### Uniqueness When working with uniqueness constraints, be careful not to pass in override values that will conflict with the generated sequence values. In this example the email will be the same for both users. If email must be unique, this code will error: ```ruby factory :user do sequence(:email) { |n| "person#{n}@example.com" } end FactoryBot.create(:user, email: "person1@example.com") FactoryBot.create(:user) ``` Traits ------ ### Defining traits Traits allow you to group attributes together and then apply them to any factory. ```ruby factory :user, aliases: [:author] factory :story do title { "My awesome story" } author trait :published do published { true } end trait :unpublished do published { false } end trait :week_long_publishing do start_at { 1.week.ago } end_at { Time.now } end trait :month_long_publishing do start_at { 1.month.ago } end_at { Time.now } end factory :week_long_published_story, traits: [:published, :week_long_publishing] factory :month_long_published_story, traits: [:published, :month_long_publishing] factory :week_long_unpublished_story, traits: [:unpublished, :week_long_publishing] factory :month_long_unpublished_story, traits: [:unpublished, :month_long_publishing] end ``` ### As implicit attributes Traits can be used as implicit attributes: ```ruby factory :week_long_published_story_with_title, parent: :story do published week_long_publishing title { "Publishing that was started at #{start_at}" } end ``` Note that defining traits as implicit attributes will not work if you have a factory or sequence with the same name as the trait. ### Attribute precedence Traits that define the same attributes won't raise AttributeDefinitionErrors; the trait that defines the attribute latest gets precedence. ```ruby factory :user do name { "Friendly User" } login { name } trait :active do name { "John Doe" } status { :active } login { "#{name} (active)" } end trait :inactive do name { "Jane Doe" } status { :inactive } login { "#{name} (inactive)" } end trait :admin do admin { true } login { "admin-#{name}" } end factory :active_admin, traits: [:active, :admin] # login will be "admin-John Doe" factory :inactive_admin, traits: [:admin, :inactive] # login will be "Jane Doe (inactive)" end ``` ### In child factories You can override individual attributes granted by a trait in a child factory: ```ruby factory :user do name { "Friendly User" } login { name } trait :active do name { "John Doe" } status { :active } login { "#{name} (M)" } end factory :brandon do active name { "Brandon" } end end ``` ### As mixins Traits can be defined outside of factories and used as mixins to compose shared attributes ```ruby FactoryBot.define do trait :timestamps do created_at { 8.days.ago } updated_at { 4.days.ago } end factory :user, traits: [:timestamps] do username { "john_doe" } end factory :post do timestamps title { "Traits rock" } end end ``` ### Using traits Traits can also be passed in as a list of symbols when you construct an instance from factory\_bot. ```ruby factory :user do name { "Friendly User" } trait :active do name { "John Doe" } status { :active } end trait :admin do admin { true } end end # creates an admin user with :active status and name "Jon Snow" create(:user, :admin, :active, name: "Jon Snow") ``` This ability works with `build`, `build_stubbed`, `attributes_for`, and `create`. `create_list` and `build_list` methods are supported as well. Just remember to pass the number of instances to create/build as second parameter, as documented in the "Building or Creating Multiple Records" section of this file. ```ruby factory :user do name { "Friendly User" } trait :admin do admin { true } end end # creates 3 admin users with :active status and name "Jon Snow" create_list(:user, 3, :admin, :active, name: "Jon Snow") ``` ### With associations Traits can be used with associations easily too: ```ruby factory :user do name { "Friendly User" } trait :admin do admin { true } end end factory :post do association :user, :admin, name: 'John Doe' end # creates an admin user with name "John Doe" create(:post).user ``` When you're using association names that're different than the factory: ```ruby factory :user do name { "Friendly User" } trait :admin do admin { true } end end factory :post do association :author, :admin, factory: :user, name: 'John Doe' # or association :author, factory: [:user, :admin], name: 'John Doe' end # creates an admin user with name "John Doe" create(:post).author ``` ### Traits within traits Traits can be used within other traits to mix in their attributes. ```ruby factory :order do trait :completed do completed_at { 3.days.ago } end trait :refunded do completed refunded_at { 1.day.ago } end end ``` ### With transient attributes Finally, traits can accept transient attributes. ```ruby factory :invoice do trait :with_amount do transient do amount { 1 } end after(:create) do |invoice, evaluator| create :line_item, invoice: invoice, amount: evaluator.amount end end end create :invoice, :with_amount, amount: 2 ``` ### Enum traits Given an Active Record model with an enum attribute: ```ruby class Task < ActiveRecord::Base enum status: {queued: 0, started: 1, finished: 2} end ``` factory\_bot will automatically define traits for each possible value of the enum: ```ruby FactoryBot.define do factory :task end FactoryBot.build(:task, :queued) FactoryBot.build(:task, :started) FactoryBot.build(:task, :finished) ``` Writing the traits out manually would be cumbersome, and is not necessary: ```ruby FactoryBot.define do factory :task do trait :queued do status { :queued } end trait :started do status { :started } end trait :finished do status { :finished } end end end ``` If automatically defining traits for enum attributes on every factory is not desired, it is possible to disable the feature by setting `FactoryBot.automatically_define_enum_traits = false` In that case, it is still possible to explicitly define traits for an enum attribute in a particular factory: ```ruby FactoryBot.automatically_define_enum_traits = false FactoryBot.define do factory :task do traits_for_enum(:status) end end ``` It is also possible to use this feature for other enumerable values, not specifically tied to Active Record enum attributes. With an array: ```ruby class Task attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum(:status, ["queued", "started", "finished"]) end end ``` Or with a hash: ```ruby class Task attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum(:status, { queued: 0, started: 1, finished: 2 }) end end ``` Callbacks --------- ### Default callbacks factory\_bot makes available four callbacks for injecting some code: * after(:build) - called after a factory is built (via `FactoryBot.build`, `FactoryBot.create`) * before(:create) - called before a factory is saved (via `FactoryBot.create`) * after(:create) - called after a factory is saved (via `FactoryBot.create`) * after(:stub) - called after a factory is stubbed (via `FactoryBot.build_stubbed`) Examples: ```ruby # Define a factory that calls the generate_hashed_password method after it is built factory :user do after(:build) { |user| generate_hashed_password(user) } end ``` Note that you'll have an instance of the user in the block. This can be useful. ### Multiple callbacks You can also define multiple types of callbacks on the same factory: ```ruby factory :user do after(:build) { |user| do_something_to(user) } after(:create) { |user| do_something_else_to(user) } end ``` Factories can also define any number of the same kind of callback. These callbacks will be executed in the order they are specified: ```ruby factory :user do after(:create) { this_runs_first } after(:create) { then_this } end ``` Calling `create` will invoke both `after_build` and `after_create` callbacks. Also, like standard attributes, child factories will inherit (and can also define) callbacks from their parent factory. Multiple callbacks can be assigned to run a block; this is useful when building various strategies that run the same code (since there are no callbacks that are shared across all strategies). ```ruby factory :user do callback(:after_stub, :before_create) { do_something } after(:stub, :create) { do_something_else } before(:create, :custom) { do_a_third_thing } end ``` ### Global callbacks To override callbacks for all factories, define them within the `FactoryBot.define` block: ```ruby FactoryBot.define do after(:build) { |object| puts "Built #{object}" } after(:create) { |object| AuditLog.create(attrs: object.attributes) } factory :user do name { "John Doe" } end end ``` ### Symbol#to_proc You can call callbacks that rely on `Symbol#to_proc`: ```ruby # app/models/user.rb class User < ActiveRecord::Base def confirm! # confirm the user account end end # spec/factories.rb FactoryBot.define do factory :user do after :create, &:confirm! end end create(:user) # creates the user and confirms it ``` Modifying factories ------------------- If you're given a set of factories (say, from a gem developer) but want to change them to fit into your application better, you can modify that factory instead of creating a child factory and adding attributes there. If a gem were to give you a User factory: ```ruby FactoryBot.define do factory :user do full_name { "John Doe" } sequence(:username) { |n| "user#{n}" } password { "password" } end end ``` Instead of creating a child factory that added additional attributes: ```ruby FactoryBot.define do factory :application_user, parent: :user do full_name { "Jane Doe" } date_of_birth { 21.years.ago } health { 90 } end end ``` You could modify that factory instead. ```ruby FactoryBot.modify do factory :user do full_name { "Jane Doe" } date_of_birth { 21.years.ago } health { 90 } end end ``` When modifying a factory, you can change any of the attributes you want (aside from callbacks). `FactoryBot.modify` must be called outside of a `FactoryBot.define` block as it operates on factories differently. A caveat: you can only modify factories (not sequences or traits) and callbacks *still compound as they normally would*. So, if the factory you're modifying defines an `after(:create)` callback, you defining an `after(:create)` won't override it, it'll just get run after the first callback. Building or Creating Multiple Records ------------------------------------- Sometimes, you'll want to create or build multiple instances of a factory at once. ```ruby built_users = build_list(:user, 25) created_users = create_list(:user, 25) ``` These methods will build or create a specific amount of factories and return them as an array. To set the attributes for each of the factories, you can pass in a hash as you normally would. ```ruby twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago) ``` In order to set different attributes for each factory, these methods may be passed a block, with the factory and the index as parameters: ```ruby twenty_somethings = build_list(:user, 10) do |user, i| user.date_of_birth = (20 + i).years.ago end ``` `create_list` passes saved instances into the block. If you modify the instance, you must save it again: ```ruby twenty_somethings = create_list(:user, 10) do |user, i| user.date_of_birth = (20 + i).years.ago user.save! end ``` `build_stubbed_list` will give you fully stubbed out instances: ```ruby stubbed_users = build_stubbed_list(:user, 25) # array of stubbed users ``` There's also a set of `*_pair` methods for creating two records at a time: ```ruby built_users = build_pair(:user) # array of two built users created_users = create_pair(:user) # array of two created users ``` If you need multiple attribute hashes, `attributes_for_list` will generate them: ```ruby users_attrs = attributes_for_list(:user, 25) # array of attribute hashes ``` Linting Factories ----------------- factory\_bot allows for linting known factories: ```ruby FactoryBot.lint ``` `FactoryBot.lint` creates each factory and catches any exceptions raised during the creation process. `FactoryBot::InvalidFactoryError` is raised with a list of factories (and corresponding exceptions) for factories which could not be created. Recommended usage of `FactoryBot.lint` is to run this in a task before your test suite is executed. Running it in a `before(:suite)`, will negatively impact the performance of your tests when running single tests. Example Rake task: ```ruby # lib/tasks/factory_bot.rake namespace :factory_bot do desc "Verify that all FactoryBot factories are valid" task lint: :environment do if Rails.env.test? conn = ActiveRecord::Base.connection conn.transaction do FactoryBot.lint raise ActiveRecord::Rollback end else system("bundle exec rake factory_bot:lint RAILS_ENV='test'") fail if $?.exitstatus.nonzero? end end end ``` After calling `FactoryBot.lint`, you'll likely want to clear out the database, as records will most likely be created. The provided example above uses an sql transaction and rollback to leave the database clean. You can lint factories selectively by passing only factories you want linted: ```ruby factories_to_lint = FactoryBot.factories.reject do |factory| factory.name =~ /^old_/ end FactoryBot.lint factories_to_lint ``` This would lint all factories that aren't prefixed with `old_`. Traits can also be linted. This option verifies that each and every trait of a factory generates a valid object on its own. This is turned on by passing `traits: true` to the `lint` method: ```ruby FactoryBot.lint traits: true ``` This can also be combined with other arguments: ```ruby FactoryBot.lint factories_to_lint, traits: true ``` You can also specify the strategy used for linting: ```ruby FactoryBot.lint strategy: :build ``` Verbose linting will include full backtraces for each error, which can be helpful for debugging: ```ruby FactoryBot.lint verbose: true ``` Custom Construction ------------------- If you want to use factory\_bot to construct an object where some attributes are passed to `initialize` or if you want to do something other than simply calling `new` on your build class, you can override the default behavior by defining `initialize_with` on your factory. Example: ```ruby # user.rb class User attr_accessor :name, :email def initialize(name) @name = name end end # factories.rb sequence(:email) { |n| "person#{n}@example.com" } factory :user do name { "Jane Doe" } email initialize_with { new(name) } end build(:user).name # Jane Doe ``` Although factory\_bot is written to work with ActiveRecord out of the box, it can also work with any Ruby class. For maximum compatibility with ActiveRecord, the default initializer builds all instances by calling `new` on your build class without any arguments. It then calls attribute writer methods to assign all the attribute values. While that works fine for ActiveRecord, it actually doesn't work for almost any other Ruby class. You can override the initializer in order to: * Build non-ActiveRecord objects that require arguments to `initialize` * Use a method other than `new` to instantiate the instance * Do wild things like decorate the instance after it's built When using `initialize_with`, you don't have to declare the class itself when calling `new`; however, any other class methods you want to call will have to be called on the class explicitly. For example: ```ruby factory :user do name { "John Doe" } initialize_with { User.build_with_name(name) } end ``` You can also access all public attributes within the `initialize_with` block by calling `attributes`: ```ruby factory :user do transient do comments_count { 5 } end name "John Doe" initialize_with { new(**attributes) } end ``` This will build a hash of all attributes to be passed to `new`. It won't include transient attributes, but everything else defined in the factory will be passed (associations, evaluated sequences, etc.) You can define `initialize_with` for all factories by including it in the `FactoryBot.define` block: ```ruby FactoryBot.define do initialize_with { new("Awesome first argument") } end ``` When using `initialize_with`, attributes accessed from within the `initialize_with` block are assigned *only* in the constructor; this equates to roughly the following code: ```ruby FactoryBot.define do factory :user do initialize_with { new(name) } name { 'value' } end end build(:user) # runs User.new('value') ``` This prevents duplicate assignment; in versions of factory\_bot before 4.0, it would run this: ```ruby FactoryBot.define do factory :user do initialize_with { new(name) } name { 'value' } end end build(:user) # runs user = User.new('value') user.name = 'value' ``` Custom Strategies ----------------- There are times where you may want to extend behavior of factory\_bot by adding a custom build strategy. Strategies define two methods: `association` and `result`. `association` receives a `FactoryBot::FactoryRunner` instance, upon which you can call `run`, overriding the strategy if you want. The second method, `result`, receives a `FactoryBot::Evaluation` instance. It provides a way to trigger callbacks (with `notify`), `object` or `hash` (to get the result instance or a hash based on the attributes defined in the factory), and `create`, which executes the `to_create` callback defined on the factory. To understand how factory\_bot uses strategies internally, it's probably easiest to just view the source for each of the four default strategies. Here's an example of composing a strategy using `FactoryBot::Strategy::Create` to build a JSON representation of your model. ```ruby class JsonStrategy def initialize @strategy = FactoryBot.strategy_by_name(:create).new end delegate :association, to: :@strategy def result(evaluation) @strategy.result(evaluation).to_json end def to_sym :json end end ``` For factory\_bot to recognize the new strategy, you can register it: ```ruby FactoryBot.register_strategy(:json, JsonStrategy) ``` This allows you to call ```ruby FactoryBot.json(:user) ``` Finally, you can override factory\_bot's own strategies if you'd like by registering a new object in place of the strategies. Custom Callbacks ---------------- Custom callbacks can be defined if you're using custom strategies: ```ruby class JsonStrategy def initialize @strategy = FactoryBot.strategy_by_name(:create).new end delegate :association, to: :@strategy def result(evaluation) result = @strategy.result(evaluation) evaluation.notify(:before_json, result) result.to_json.tap do |json| evaluation.notify(:after_json, json) evaluation.notify(:make_json_awesome, json) end end def to_sym :json end end FactoryBot.register_strategy(:json, JsonStrategy) FactoryBot.define do factory :user do before(:json) { |user| do_something_to(user) } after(:json) { |user_json| do_something_to(user_json) } callback(:make_json_awesome) { |user_json| do_something_to(user_json) } end end ``` Custom Methods to Persist Objects --------------------------------- By default, creating a record will call `save!` on the instance; since this may not always be ideal, you can override that behavior by defining `to_create` on the factory: ```ruby factory :different_orm_model do to_create { |instance| instance.persist! } end ``` To disable the persistence method altogether on create, you can `skip_create` for that factory: ```ruby factory :user_without_database do skip_create end ``` To override `to_create` for all factories, define it within the `FactoryBot.define` block: ```ruby FactoryBot.define do to_create { |instance| instance.persist! } factory :user do name { "John Doe" } end end ``` ActiveSupport Instrumentation ----------------------------- In order to track what factories are created (and with what build strategy), `ActiveSupport::Notifications` are included to provide a way to subscribe to factories being compiled and run. One example would be to track factories based on a threshold of execution time. ```ruby ActiveSupport::Notifications.subscribe("factory_bot.run_factory") do |name, start, finish, id, payload| execution_time_in_seconds = finish - start if execution_time_in_seconds >= 0.5 $stderr.puts "Slow factory: #{payload[:name]} using strategy #{payload[:strategy]}" end end ``` Another example would be tracking all factories and how they're used throughout your test suite. If you're using RSpec, it's as simple as adding a `before(:suite)` and `after(:suite)`: ```ruby factory_bot_results = {} config.before(:suite) do ActiveSupport::Notifications.subscribe("factory_bot.run_factory") do |name, start, finish, id, payload| factory_name = payload[:name] strategy_name = payload[:strategy] factory_bot_results[factory_name] ||= {} factory_bot_results[factory_name][strategy_name] ||= 0 factory_bot_results[factory_name][strategy_name] += 1 end end config.after(:suite) do puts factory_bot_results end ``` Another example could involve tracking the attributes and traits that factories are compiled with. If you're using RSpec, you could add `before(:suite)` and `after(:suite)` blocks that subscribe to `factory_bot.compile_factory` notifications: ```ruby factory_bot_results = {} config.before(:suite) do ActiveSupport::Notifications.subscribe("factory_bot.compile_factory") do |name, start, finish, id, payload| factory_name = payload[:name] factory_class = payload[:class] attributes = payload[:attributes] traits = payload[:traits] factory_bot_results[factory_class] ||= {} factory_bot_results[factory_class][factory_name] = { attributes: attributes.map(&:name) traits: traits.map(&:name) } end end config.after(:suite) do puts factory_bot_results end ``` Rails Preloaders and RSpec -------------------------- When running RSpec with a Rails preloader such as `spring` or `zeus`, it's possible to encounter an `ActiveRecord::AssociationTypeMismatch` error when creating a factory with associations, as below: ```ruby FactoryBot.define do factory :united_states, class: "Location" do name { 'United States' } association :location_group, factory: :north_america end factory :north_america, class: "LocationGroup" do name { 'North America' } end end ``` The error occurs during the run of the test suite: ``` Failure/Error: united_states = create(:united_states) ActiveRecord::AssociationTypeMismatch: LocationGroup(#70251250797320) expected, got LocationGroup(#70251200725840) ``` The two possible solutions are to either run the suite without the preloader, or to add `FactoryBot.reload` to the RSpec configuration, like so: ```ruby RSpec.configure do |config| config.before(:suite) { FactoryBot.reload } end ``` Using Without Bundler --------------------- If you're not using Bundler, be sure to have the gem installed and call: ```ruby require 'factory_bot' ``` Once required, assuming you have a directory structure of `spec/factories` or `test/factories`, all you'll need to do is run: ```ruby FactoryBot.find_definitions ``` If you're using a separate directory structure for your factories, you can change the definition file paths before trying to find definitions: ```ruby FactoryBot.definition_file_paths = %w(custom_factories_directory) FactoryBot.find_definitions ``` If you don't have a separate directory of factories and would like to define them inline, that's possible as well: ```ruby require 'factory_bot' FactoryBot.define do factory :user do name { 'John Doe' } date_of_birth { 21.years.ago } end end ``` factory_bot-6.5.6/Gemfile000066400000000000000000000002321507676517300153540ustar00rootroot00000000000000source "https://rubygems.org" gem "activerecord-jdbcsqlite3-adapter", platforms: [:jruby] gem "sqlite3", platforms: [:ruby] gemspec name: "factory_bot" factory_bot-6.5.6/Gemfile.lock000066400000000000000000000107441507676517300163140ustar00rootroot00000000000000PATH remote: . specs: factory_bot (6.5.6) activesupport (>= 6.1.0) GEM remote: https://rubygems.org/ specs: activemodel (8.0.3) activesupport (= 8.0.3) activerecord (8.0.3) activemodel (= 8.0.3) activesupport (= 8.0.3) timeout (>= 0.4.0) activesupport (8.0.3) base64 benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) logger (>= 1.4.2) minitest (>= 5.1) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) uri (>= 0.13.1) appraisal (2.5.0) bundler rake thor (>= 0.14.0) aruba (2.3.2) bundler (>= 1.17, < 3.0) contracts (>= 0.16.0, < 0.18.0) cucumber (>= 8.0, < 11.0) rspec-expectations (>= 3.4, < 5.0) thor (~> 1.0) ast (2.4.3) base64 (0.3.0) benchmark (0.4.1) bigdecimal (3.3.1) builder (3.3.0) concurrent-ruby (1.3.5) connection_pool (2.5.4) contracts (0.17.2) cucumber (10.1.1) base64 (~> 0.2) builder (~> 3.2) cucumber-ci-environment (> 9, < 11) cucumber-core (> 15, < 17) cucumber-cucumber-expressions (> 17, < 19) cucumber-html-formatter (> 20.3, < 22) diff-lcs (~> 1.5) logger (~> 1.6) mini_mime (~> 1.1) multi_test (~> 1.1) sys-uname (~> 1.3) cucumber-ci-environment (10.0.1) cucumber-core (15.2.1) cucumber-gherkin (> 27, < 33) cucumber-messages (> 26, < 30) cucumber-tag-expressions (> 5, < 7) cucumber-cucumber-expressions (18.0.1) bigdecimal cucumber-gherkin (32.2.0) cucumber-messages (> 25, < 28) cucumber-html-formatter (21.15.1) cucumber-messages (> 19, < 28) cucumber-messages (27.2.0) cucumber-tag-expressions (6.1.2) diff-lcs (1.6.2) docile (1.4.1) drb (2.2.3) ffi (1.17.2) i18n (1.14.7) concurrent-ruby (~> 1.0) json (2.15.1) language_server-protocol (3.17.0.5) lint_roller (1.1.0) logger (1.7.0) memoist3 (1.0.0) mini_mime (1.1.5) mini_portile2 (2.8.8) minitest (5.26.0) multi_test (1.1.0) mutex_m (0.3.0) ostruct (0.6.3) parallel (1.27.0) parser (3.3.9.0) ast (~> 2.4.1) racc prism (1.5.2) racc (1.8.1) rainbow (3.1.1) rake (13.3.0) regexp_parser (2.11.3) rspec (3.13.1) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) rspec-mocks (~> 3.13.0) rspec-core (3.13.5) rspec-support (~> 3.13.0) rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-its (2.0.0) rspec-core (>= 3.13.0) rspec-expectations (>= 3.13.0) rspec-mocks (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.6) rubocop (1.80.2) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) rubocop-ast (>= 1.46.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) rubocop-ast (1.47.1) parser (>= 3.3.7.2) prism (~> 1.4) rubocop-performance (1.25.0) lint_roller (~> 1.1) rubocop (>= 1.75.0, < 2.0) rubocop-ast (>= 1.38.0, < 2.0) ruby-progressbar (1.13.0) securerandom (0.4.1) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) sqlite3 (2.5.0) mini_portile2 (~> 2.8.0) standard (1.51.1) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) rubocop (~> 1.80.2) standard-custom (~> 1.0.0) standard-performance (~> 1.8) standard-custom (1.0.2) lint_roller (~> 1.0) rubocop (~> 1.50) standard-performance (1.8.0) lint_roller (~> 1.1) rubocop-performance (~> 1.25.0) sys-uname (1.4.1) ffi (~> 1.1) memoist3 (~> 1.0.0) thor (1.4.0) timeout (0.4.3) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.1.0) uri (1.0.4) yard (0.9.37) PLATFORMS ruby DEPENDENCIES activerecord activerecord-jdbcsqlite3-adapter appraisal aruba factory_bot! mutex_m ostruct rake rspec rspec-its simplecov sqlite3 standard yard BUNDLED WITH 2.7.2 factory_bot-6.5.6/LICENSE000066400000000000000000000020701507676517300150700ustar00rootroot00000000000000Copyright (c) 2008-2019 Joe Ferris and thoughtbot, inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. factory_bot-6.5.6/NAME.md000066400000000000000000000014111507676517300151230ustar00rootroot00000000000000# Project Naming History ## Factory Girl This library was [initially released](https://robots.thoughtbot.com/waiting-for-a-factory-girl) in 2008 with the name "Factory Girl". We chose the name as a nod in the direction of the [Factory method](https://en.wikipedia.org/wiki/Factory_method_pattern) and [Object Mother](http://martinfowler.com/bliki/ObjectMother.html) software patterns from the _Design Patterns_ book, and as a reference to the [Rolling Stones song](https://www.youtube.com/watch?v=4jKix2DFlnA) of the same name. ## Factory Bot The name "Factory Girl" was confusing to some developers who encountered this library, and offensive or problematic to others. In October 2017 we [renamed the library](https://robots.thoughtbot.com/factory_bot) to "Factory Bot". factory_bot-6.5.6/NEWS.md000066400000000000000000000635221507676517300151720ustar00rootroot00000000000000# News ## 6.5.6 (October 22, 2025) * Fix: Enforce association override precedence over trait foreign keys by @JinOketani in [#1768](https://github.com/thoughtbot/factory_bot/pull/1768) * Build: Add ostruct as a development dependency by @ydah in [#1778](https://github.com/thoughtbot/factory_bot/pull/1778) * Build: Bump standard from v1.44.0 to v1.51.1 by @ydah in [#1779](https://github.com/thoughtbot/factory_bot/pull/1779) * Build: Add Ruby 3.4 to CI matrix by @ydah in [#1780](https://github.com/thoughtbot/factory_bot/pull/1780) * Build: Remove unnecessary development dependencies by @ydah in [#1781](https://github.com/thoughtbot/factory_bot/pull/1781) * Build: update gem versions and dependencies by @ydah in [#1782](https://github.com/thoughtbot/factory_bot/pull/1782) * Build: revert removal of mutex_m by @vburzynski in [#1784](https://github.com/thoughtbot/factory_bot/pull/1784) * Refactor: factory calculator cleanup by @vburzynski in [#1770](https://github.com/thoughtbot/factory_bot/pull/1770) * Chore(ci): Bump actions/checkout from 4 to 5 by @dependabot[bot] in [#1765](https://github.com/thoughtbot/factory_bot/pull/1765) * Chore(specs): tag slow specs by @vburzynski in [#1776](https://github.com/thoughtbot/factory_bot/pull/1776) * Docs: Update RELEASING.md by @vburzynski in [#1763](https://github.com/thoughtbot/factory_bot/pull/1763) * Docs: Update link to FactoryGirl upgrade guide by @imRohan in [#1769](https://github.com/thoughtbot/factory_bot/pull/1769) * Docs: Fix some typos by @ydah in [#1783](https://github.com/thoughtbot/factory_bot/pull/1783) * Docs(yard): resolve yard doc warnings by @vburzynski in [#1764](https://github.com/thoughtbot/factory_bot/pull/1764) * Docs(yard): ruby syntax highlighting in yard docs by @djbender in [#1777](https://github.com/thoughtbot/factory_bot/pull/1777) ## 6.5.5 (August 15, 2025) * Feat: Adds developer console features (CodeMeister) * adds `bin/setup` to run bundle install * adds `bin/console` to open an IRB console with factory_bot loaded * adds a session method `reload!` will reload all gem files. * Feat: Before(:all) and after(:all) callbacks added (CodeMeister) * Feat: Adds support for before build callback (Mohammed Nasser and Neil Carvalho) * Feat: Adds support for lazy loaded initial value sequences (AJ Esler) * Fix: Refactors the removal of files from the loaded register in `.irbrc` (CodeMeister) * Fix: Improves the sequence handling with better context support (CodeMeister) * Fixes issue #1754 where `#generate` was throwing an error * Refactors the `#increment_sequence` method to remove the URI requirement * Fix: Cleans up ActiveSupport core extensions (Neil Carvalho) * Fix: Addresses issue #1709 with resolution of conflict between '' and '` (CodeMeister) * Fix: Addresses issue #1712 by ensuring callbacks only run once per instance (CodeMeister) * Docs: Downcase of the word "constants" in `.irbrc` file (CodeMeister) * Docs: Update docs with note clarifying when the full namespace is required in a factory (John Pitchko) * Chore: Add new maintainers to CODEOWNERS (Ajina Slater) ## 6.5.4 (June 13, 2025) * Fix bug where user-defined method named `definition` could not be set through `method_missing` in factories. (CodeMeister) ## 6.5.3 (June 2, 2025) * Fix: Factory sequences without blocks (CodeMeister) * Added: New methods for setting, generating and rewinding sequences (CodeMeister) ## 6.5.2 (May 30, 2025) * Changed: Updated "verbose linting" test to allow for backtrace changes in Ruby 3.4 (CodeMeister) * Fix: Set the same timestamps for `created_at` and `updated_at` on `build_stubbed` (Kim Emmanuel) * Fix: Refactored sequences to ensure cloned traits use parent sequences. (CodeMeister) * Docs: Fix definition_file_paths comment (Milo Winningham) * Docs: Add ruby-lsp extensions to Useful Tools in README.md (johansenja) * Docs: Fix docs about definition file paths (Ryo Nakamura) * Docs: Update has_many-associations.md to mention that traits can use inline associations (Matthew Zagaja) * Docs: Fix "Transitioning from Factory Girl" guide link (Neil Carvalho) ## 6.5.1 (January 31, 2025) * Changed: execute linting tests within ActiveRecord transactions when available (Sean Doyle) * Fix: Random test failure when tracking compilation time (CodeMeister) * Fix: Bump the minimum required activesupport version to 6.1 (Earlopain) * Internal: Update development dependencies (Neil Carvalho) ## 6.5.0 (September 6, 2024) * fix: issue 1621 broken links in ref/factory.md by @elasticspoon in https://github.com/thoughtbot/factory_bot/pull/1623 * Add standard settings by @ydah in https://github.com/thoughtbot/factory_bot/pull/1625 * Call dynamic-readme reusable workflow by @stefannibrasil in https://github.com/thoughtbot/factory_bot/pull/1628 * Update README again by @stefannibrasil in https://github.com/thoughtbot/factory_bot/pull/1630 * Only run this workflow if the README has been updated by @stefannibrasil in https://github.com/thoughtbot/factory_bot/pull/1635 * Automatically Generated: Update Dynamic Section in README by @github-actions in https://github.com/thoughtbot/factory_bot/pull/1637 * Added a case for build_class to handle class names with underscores passed as a string by @m-gizzi in https://github.com/thoughtbot/factory_bot/pull/1642 * Add Ruby 3.3 to CI by @berkos in https://github.com/thoughtbot/factory_bot/pull/1615 * Update Dependabot configuration by @smaboshe in https://github.com/thoughtbot/factory_bot/pull/1655 * Add new maintainers to CODEOWNERS by @sarahraqueld in https://github.com/thoughtbot/factory_bot/pull/1651 * Improve docs formatting and fix filename conflicts by @sarahraqueld in https://github.com/thoughtbot/factory_bot/pull/1666 * Add a dynamic security workflow and a SECURITY.md file by @sarahraqueld in https://github.com/thoughtbot/factory_bot/pull/1677 * Automatically Generated: Update Dynamic Section in SECURITY by @github-actions in https://github.com/thoughtbot/factory_bot/pull/1678 * Ensure rails 7.2 compatibility by @Earlopain in https://github.com/thoughtbot/factory_bot/pull/1686 * Fix the factory definition in traits documentation by @ddieulivol in https://github.com/thoughtbot/factory_bot/pull/1688 ## 6.4.6 (January 30, 2023) * Fix: Bump minimum required Ruby in gemspec (Earlopain). * Fix: Broken link in `FactoryBot.modify` docs (Matt Brictson). * Fix: Broken link in `FactoryBot.lint` docs (Anton Dieterle). ## 6.4.5 (December 29, 2023) * Changed: Support Ruby 3.0+, Rails 6.1+ (Mike Burns). ## 6.4.4 (December 27, 2023) * Internal: Remove observer dependency (Earlopain). ## 6.4.3 (December 26, 2023) * Fix: Support models without ID setters in build_stubbed (Olivier Bellone). * Fix: Explicit observer dependency (Oleg Antonyan). * Internal: Add Rails 7.1 to CI (Olivier Bellone). * Internal: Bump github actions/checkout to v4 (Lorenzo Zabot) * Internal: Stop passing disable-error_highlight in CI (Mike Burns). * Internal: Relax the exception message check (Mike Burns). ## 6.4.2 (November 22, 2023) * Fix: top-level traits pass their class to ActiveSupport::Notifications (makicamel). ## 6.4.1 (November 20, 2023) * Fix: factories with traits pass their class to ActiveSupport::Notifications (makicamel). ## 6.4.0 (November 17, 2023) * Added: if `build_stubbed` detects a UUID primary key, generate the correct type (Peter Boling, Alexandre Ruban). * Docs: show examples of Ruby 3 syntactic sugars (Sean Doyle). * Internal: resolve test warning messages (Mike Burns). ## 6.3.0 (September 1, 2023) * Fix: link to changelog for RubyGems (Berkan Ünal). * Fix: integrate with Ruby 3.2's `did_you_mean` library (Daniel Colson). * Changed: explicitly define `#destroyed?` within the `Stub` strategy to return `false` to be consistent with ActiveRecord (Benjamin Fleischer). * Added: announce `factory_bot.compile_factory` notification (Sean Doyle). * Docs: clarify that custom strategies need to define `#to_sym` (Edmund Korley, Jonas S). * Docs: fix CI link in README (Mark Huk). * Docs: fix GitHub links (Robert Fletcher). * Docs: install this library with `bundle add` (Glauco Custódio). * Docs: re-write into mdBook (Mike Burns, Sara Jackson, Stefanni Brasil) * Docs: clarify that automatic trait definitions could introduce new linting errors (Lawrence Chou). * Internal: skip TruffleRuby on Rails 5.0, 5.1, 5.2 (Andrii Konchyn). * Internal: fix typos throughout codebase (Yudai Takada). * Internal: run CI on `actions/checkout` v3 (Yudai Takada). * Internal: follow standardrb code style (Yudai Takada). * Internal: stop using Hound (Daniel Nolan). * Internal: only run simplecov on C Ruby (Daniel Colson). * Internal: quieter Cucumber (Daniel Colson). * Internal: Ruby 3.2 support (Daniel Colson). * Internal: Mike Burns is the CODEOWNER (Stefanni Brasil). ## 6.2.1 (March 8, 2022) * Added: CI testing against truffleruby * Changed: Documentation improvements for sequences and traits * Fixed: ActiveSupport::Notifications reporting strategy through associations now report as symbols * BREAKING CHANGE: Custom strategies now need to define a `to_sym` method to specify the strategy identifier * Fixed: `add_attribute` with reserved keywords assigns values correctly ## 6.2.0 (May 7, 2021) * Added: support for Ruby 3.0 * Changed: Include factory or trait name in error messages for missing traits. d05a9a3c * Changed: Switched from Travis CI to GitHub Actions * Fixed: More Ruby 2.7 kwarg deprecation warnings ## 6.1.0 (July 8, 2020) * Added: public reader for the evaluation instance, helpful for building interrelated associations * Changed: raise a more helpful error when passing an invalid argument to an association * Fixed: Ruby 2.7 kwarg deprecation warnings ## 6.0.2 (June 19, 2020) * Fixed: bug causing traits to consume more memory each time they were used ## 6.0.1 (June 19, 2020) * Fixed: bug with constant resolution causing unexpected uninitialized constant errors ## 6.0.0 (June 18, 2020) * Added: automatic definition of traits for Active Record enum attributes, enabled by default (Note that this required changing where factory_bot constantizes the build class, which may affect applications that were using abstract factories for inheritance. See issue #1409.) (This may break `FactoryBot.lint` because there may be previously non-existing factory+trait combinations being defined and checked) * Added: `traits_for_enum` method to define traits for non-Active Record enums * Added: `build_stubbed_starting_id=` option to define the starting id for `build_stubbed` * Removed: deprecated methods on the top-level `FactoryBot` module meant only for internal use * Removed: support for EOL versions of Ruby (2.3, 2.4) and Rails (4.2) * Removed: support for "abstract" factories with no associated class; use traits instead. ## 5.2.0 (April 24, 2020) * Added: Pass index to block for `*_list` methods * Deprecated: methods on the top-level `FactoryBot` module meant only for internal use: `callbacks`, `configuration`, `constructor`, `initialize_with`, `register_sequence`, `resent_configuration`, `skip_create`, `to_create` ## 5.1.2 (March 25, 2020) * Fixed: Ruby 2.7 keyword deprecation warning in FactoryBot.lint ## 5.1.1 (October 2, 2019) * Improved: performance of traits * Fixed: registering strategies on JRuby ## 5.1.0 (September 21, 2019) * Added: "Did you mean?" style error message to help with typos in association declarations * Changed: `NoMethodError` for static attributes now offers a "Did you mean?" style message * Fixed: avoid undefining inherited evaluator methods * Fixed: avoid stubbing id for records without a primary key * Fixed: raise a helpful error for self-referencing traits to avoid a `SystemStackError` * Deprecated: methods on the top-level `FactoryBot` module meant only for internal use: `allow_class_lookup`, `allow_class_lookup`=, `register_trait`, `trait_by_name`, `traits`, `sequence_by_name`, `sequences`, `factory_by_name`, `register_factory`, `callback_names`, `register_callback`, `register_default_callbacks`, `register_default_strategies`, `strategies` ## 5.0.2 (February 22, 2019) * Bugfix: raise "Trait not registered" error when passing invalid trait arguments ## 5.0.1 (February 15, 2019) * Bugfix: Do not raise error when two sequences have the same name in two traits that have the same name ## 5.0.0 (February 1, 2019) * Added: Verbose option to include full backtraces in the linting output * Changed: use_parent_strategy now defaults to true, so by default the build strategy will build, rather than create associations * Changed: Passing a block when defining associations now raises an error * Bugfix: use_parent_strategy is no longer reset by FactoryBot.reload * Bugfix: rewind_sequences will now rewind local sequences along with the global ones * Bugfix: the build_stubbed strategy now sets timestamps without changing the the original behavior of the timestamp methods * Bugfix: avoid a stack error when referring to an "attributes" attribute in initialize_with * Removed: support for EOL versions of Ruby and Rails * Removed: static attributes (use dynamic attributes with a block instead) * Removed: looking up factories by class * Removed: ignore method (use transient instead) * Removed: duplicate_attribute_assignment_from_initialize_with configuration option * Deprecated: allow_class_lookup configuration option ## 4.11.1 (September 7, 2018) * Documentation: Include .yardopts in the gem to fix broken RubyDoc links ## 4.11.0 (August, 15, 2018) * Bugfix: Do not raise error for valid build_stubbed methods: decrement, increment, and toggle * Bugfix: Do not add timestamps with build_stubbed for objects that shouldn't have timestamps * Deprecate static attributes ## 4.10.0 (May 25, 2018) * Allow sequences to be rewound ## 4.9.0 (skipped - FactoryGirl only release) ## 4.8.2 (October 20, 2017) * Rename factory_girl to factory_bot ## 4.8.1 (September 28, 2017) * Explicitly define `#destroyed?` within the `Stub` strategy to return `nil` instead of raising * Update various dependencies * Update internal test suite to use RSpec's mocking/stubbing instead of mocha ## 4.8.0 (December 16, 2016) * Improve documentation * Add `FactoryGirl.generate_list` to be consistent with `build_list`/`create_list` and friends * Add `FactoryGirl.use_parent_strategy` configuration to allow associations to leverage parent build strategy ## 4.7.0 (April 1, 2016) * Improve documentation * Improve instrumentation payload to include traits, overrides, and the factory itself * Allow linting of traits * Deprecate factory lookup by class name in preparation for 5.0 * Improve internal performance by using flat_map instead of map and compact * Improve handling of dirty attributes after building a stubbed object * Reduce warnings from redefining methods ## 4.6.0 (skipped) ## 4.5.0 (October 17, 2014) * Improve FactoryGirl.lint by including exception and message in output * Allow selective linting * Use more explicit #public_send when doing attribute assignment * Improve documentation around FactoryGirl.lint and initialize_with * Deprecate #ignore in favor of #transient ## 4.4.0 (February 10, 2014) * Add FactoryGirl.lint * Fix memory leak in duplicate traits * Update documentation ## 4.3.0 (November 3, 2013) * Start testing against Rails 4.0 and Ruby 2.0.0 * Stop testing against Rails 3.0 and Ruby 1.9.2 * Add `*_pair` methods to only build two objects * Raise if a method is defined with a FactoryGirl block (factory or trait) * Allow use of Symbol#to_proc in callbacks * Add global callbacks * Improve GETTING_STARTED and README ## 4.2.0 (January 18, 2013) * Improve documentation * Allow `*_list` syntax methods to accept a block * Update gem dependencies * Allow setting id for objects created with `build_stubbed` * Fix Stub strategy to mimic ActiveRecord regarding `created_at` * Evaluate sequences within the context of an Evaluator * Fix Mocha deprecation warning * Fix some warnings when running RUBYOPT=-w rake * Convert test suite to RSpec's "expect" syntax ## 4.1.0 (September 11, 2012) * Allow multiple callbacks to bind to the same block * Fix documentation surrounding the stub strategy ## 4.0.0 (August 3, 2012) * Remove deprecated cucumber_steps * Remove deprecated alternate syntaxes * Deprecate duplicate_attribute_assignment_from_initialize_with, which is now unused as attributes assigned within initialize_with are not subsequently assigned ## 3.6.1 (August 2, 2012) Update README to include info about running with JRuby * Update dependencies on RSpec and tiny versions of Rails in Appraisal * Improve flexibility of using traits with associations and add documentation * Stub update_column to raise to mirror ActiveRecord's change from update_attribute ## 3.6.0 (July 27, 2012) * Code/spec cleanup * Allow factories with traits to be used in associations * Refactor Factory to use DefinitionHierarchy to handle managing callbacks, custom constructor, and custom to_create * Add memoization to speed up factories providing attribute overrides * Add initial support of JRuby when running in 1.9 mode * Improve docs on what happens when including FactoryGirl::Syntax::Methods ## 3.5.0 (June 22, 2012) * Allow created_at to be set when using build_stubbed * Deprecate FactoryGirl step definitions ## 3.4.2 (June 19, 2012) * Fix bug in traits with callbacks called implicitly in factories whose callbacks trigger multiple times ## 3.4.1 (June 18, 2012) * Fix traits so they can be nested and referred to from other traits ## 3.4.0 (June 11, 2012) * Sequences support Enumerators * Optionally disable duplicate assignment of attributes in initialize_with * Make hash of public attributes available in initialize_with * Support referring to a factory based on class name ## 3.3.0 (May 13, 2012) * Allow to_create, skip_create, and initialize_with to be defined globally * Allow to_create, skip_create, and initialize_with to be defined within traits * Fix deprecation messages for alternate syntaxes (make, generate, etc.) * Improve library documentation * Deprecate after_build, after_create, before_create, after_stub in favor of new callbacks * Introduce new callback syntax: after(:build) {}, after(:custom) {}, or callback(:different) {} This allows for declaring any callback, usable with custom strategies * Add attributes_for_list and build_stubbed_list with the StrategySyntaxMethodRegistrar * Allow use of syntax methods (build, create, generate, etc) implicitly in callbacks * Internal refactoring of a handful of components ## 3.2.0 (April 24, 2012) * Use AS::Notifications for pub/sub to track running factories * Call new within initialize_with implicitly on the build class * Skip to_create with skip_create * Allow registration of custom strategies * Deprecate alternate syntaxes * Implicitly call factory_bot's syntax methods from dynamic attributes ## 3.1.0 (April 6, 2012) * Sequences support aliases, which reference the same block * Update documentation * Add before_create callback * Support use of #attribute_names method to determine available attributes for steps * Use ActiveSupport::Deprecation for all deprecations ## 3.0.0 (March 23, 2012) * Deprecate the vintage syntax * Remove Rails 2.x support * Remove Ruby 1.8 support * Remove deprecated features, including default_strategy, factory_name, :method for defining default strategy, ignore on individual attributes, and interacting with Factory the way you would FactoryGirl ## 2.6.4 (March 16, 2012) * Do not ignore names of transient attributes * Ensure attributes set on instance are calculated uniquely ## 2.6.3 (March 9, 2012) * Fix issue with traits not being present the first time a factory is accessed * Update available Cucumber step definitions to not require a trailing colon when building a table of attributes to instantiate records with ## 2.6.2 (March 9, 2012) * Allow factories to use all their ancestors' traits * Ignore bin dir generated by bundler * Namespace ::Factory as top-level to fix vintage syntax issue with Ruby 1.9.2-p3p18 ## 2.6.1 (March 2, 2012) * Use FactoryGirl.reload in specs * Clean up running named factories with a particular strategy with FactoryGirl::FactoryRunner ## 2.6.0 (February 17, 2012) * Improve documentation of has_many associations in the GETTING_STARTED document * Deprecate :method in favor of :strategy when overriding an association's build strategy ## 2.5.2 (February 10, 2012) * Fix step definitions to use associations defined in parent factories * Add inline trait support to (build|create)_list * Update ActiveSupport dependency to >= 2.3.9, which introduced class_attribute ## 2.5.1 (February 3, 2012) * Fix attribute evaluation when the attribute isn't defined in the factory but is a private method on Object * Update rubygems on Travis before running tests * Fix spec name * Update GETTING_STARTED with correct usage of build_stubbed * Update README with more info on initialize_with * Honor :parent on factory over block nesting ## 2.5.0 (January 20, 2012) * Revert 'Deprecate build_stubbed and attributes_for' * Implement initialize_with to allow overriding object instantiation * Ensure FG runs against Rails 3.2.0 ## 2.4.2 (January 18, 2012) * Fix inline traits' interaction with defaults on the factory ## 2.4.1 (January 17, 2012) * Deprecate build_stubbed and attributes_for * Fix inline traits ## 2.4.0 (January 13, 2012) * Refactor internals of FactoryGirl to use anonymous class on which attributes get defined * Explicitly require Ruby 1.8.7 or higher in gemspec * Fix documentation * Add Gemnasium status to documentation * Supplying a Class to a factory that overrides to_s no longer results in getting the wrong Class constructed * Be more agnostic about ORMs when using columns in FactoryGirl step definitions * Test against Active Record 3.2.0.rc2 * Update GETTING_STARTED to use Ruby syntax highlighting ## 2.3.2 (November 26, 2011) * Move logic of where instance.save! is set to Definition * Fix method name from aliases_for? to alias_for? * Refactor internal attribute handling to use an anonymous class instead of faking Ruby's variable resolution. This allows for more sane usage of attributes without having to manage sorting priority because attributes can turn themselves into procs, which are used with define_method on a class so attributes work correctly all the time. ## 2.3.1 (November 23, 2011) * Remove internally-used associate method from all the FactoryGirl::Proxy subclasses * Move around requiring of files * Consolidate errors into factory_bot.rb * Refactor AttributeList to deal with priority only when iterating over attributes * Refactor internals of some of the Proxy subclasses * Ensure callbacks on traits are executed in the correct order ## 2.3.0 (November 18, 2011) * Registries are named, resulting in better messages when factories, traits, or sequences cannot be found * Fix incorrect tests * Internals refactoring introducing FactoryGirl::NullFactory, FactoryGirl::Definition, and FactoryGirl::DeclarationList * Use ActiveSupport for Hash#except and its delegation capabilities * Fix usage of callbacks when added via implicit traits * Use Bundler tasks and clean up dependencies * Fix failing spec for big letters in factory name passed as symbol * Add ability for traits to be added dynamically when creating an instance via build, create, build_stubbed, or attributes_for ## 2.2.0 (October 14, 2011) * Clean up RSpec suite to not use 'should' * Use create_list in step definitions * Syntax methods that deal with ORM interaction (attributes_for, build, build_stubbed, and create) now accept a block that yields the result. This results in a more convenient way to interact with the result than using Object.tap. * Standardize deprecation warnings * Update transient attribute syntax to use blocks instead of calling ignore on each attribute declaration * Parents can be defined after children because factories are evaluated when they're used; this means breaking up factories across multiple files will behave as expected * Large internal refactoring, including changing access modifiers for a handful of methods for a more clearly defined API ## 2.1.2 (September 23, 2011) * Bugfix: Vintage syntax fixed after bug introduced in 2.1.1 * Introduce dependency on activesupport to remove code from Factory class ## 2.1.1 (September 23, 2011) (yanked) * Bugfix: Parent object callbacks are run before child object callbacks * Declarations: allow overriding/modification of individual traits in child factories * Callbacks refactored to not be attributes * Updating documentation for formatting and clarity (incl. new specificity for cucumber) ## 2.1.0 (September 02, 2011) * Bugfix: created_at now defined for stubbed models * Gemspec updated for use with Rails 3.1 * Factories can now be modified post-definition (useful for overriding defaults from gems/plugins) * All factories can now be reloaded with Factory.reload * Add :method => build to factory associations to prevent saving of associated objects * Factories defined in {Rails.root}/factories are now loaded by default * Various documentation updates ## 1.1.4 (November 28, 2008) * Factory.build now uses Factory.create for associations of the built object * Factory definitions are now detected in subdirectories, such as factories/person_factory.rb (thanks to Josh Nichols) * Factory definitions are now loaded after the environment in a Rails project (fixes some issues with dependencies being loaded too early) (thanks to Josh Nichols) * Factory names ending in 's' no longer cause problems (thanks to Alex Sharp and Josh Owens) ## 1.1.3 (September 12, 2008) * Automatically pull in definitions from factories.rb, test/factories.rb, or spec/factories.rb ## 1.1.2 (July 30, 2008) * Improved error handling for invalid and undefined factories/attributes * Improved handling of strings vs symbols vs classes * Added a prettier syntax for handling associations * Updated documentation and fixed compatibility with Rails 2.1 ## 1.1.1 (June 23, 2008) * The attribute "name" no longer requires using #add_attribute ## 1.1.0 (June 03, 2008) * Added support for dependent attributes * Fixed the attributes_for build strategy to not build associations * Added support for sequences ## 1.0.0 (May 31, 2008) * First version factory_bot-6.5.6/README.md000066400000000000000000000101231507676517300153400ustar00rootroot00000000000000# factory_bot [![Build Status][ci-image]][ci] [![Code Climate][grade-image]][grade] [![Gem Version][version-image]][version] factory_bot is a fixtures replacement with a straightforward definition syntax, support for multiple build strategies (saved instances, unsaved instances, attribute hashes, and stubbed objects), and support for multiple factories for the same class (user, admin_user, and so on), including factory inheritance. If you want to use factory_bot with Rails, see [factory_bot_rails](https://github.com/thoughtbot/factory_bot_rails). Interested in the history of the project name? You can find the history [here](https://github.com/thoughtbot/factory_bot/blob/main/NAME.md) Transitioning from factory\_girl? Check out the [upgrade guide](https://github.com/thoughtbot/factory_bot/blob/v4.9.0/UPGRADE_FROM_FACTORY_GIRL.md). ## Documentation See our extensive reference, guides, and cookbook in [the factory_bot book][]. For information on integrations with third party libraries, such as RSpec or Rails, see [the factory_bot wiki][]. We also have [a detailed introductory video][], available for free on Upcase. [a detailed introductory video]: https://upcase.com/videos/factory-bot?utm_source=github&utm_medium=open-source&utm_campaign=factory-girl [the factory_bot book]: https://thoughtbot.github.io/factory_bot [the factory_bot wiki]: https://github.com/thoughtbot/factory_bot/wiki ## Install Run: ```ruby bundle add factory_bot ``` To install the gem manually from your shell, run: ```shell gem install factory_bot ``` ## Supported Ruby versions Supported Ruby versions are listed in `.github/workflows/build.yml` ([source](https://github.com/thoughtbot/factory_bot/blob/main/.github/workflows/build.yml)) ## More Information * [Rubygems](https://rubygems.org/gems/factory_bot) * [Stack Overflow](https://stackoverflow.com/questions/tagged/factory-bot) * [Issues](https://github.com/thoughtbot/factory_bot/issues) * [GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS](https://robots.thoughtbot.com/) [GETTING_STARTED]: https://github.com/thoughtbot/factory_bot/blob/main/GETTING_STARTED.md ## Useful Tools * [FactoryTrace](https://github.com/djezzzl/factory_trace) - helps to find unused factories and traits. * [ruby-lsp-factory_bot](https://github.com/donny741/ruby-lsp-factory_bot) / [ruby-lsp-rails-factory-bot](https://github.com/johansenja/ruby-lsp-rails-factory-bot) - integration with [ruby-lsp](https://github.com/Shopify/ruby-lsp) to provide intellisense Contributing ------------ Please see [CONTRIBUTING.md](https://github.com/thoughtbot/factory_bot/blob/main/CONTRIBUTING.md). factory_bot was originally written by Joe Ferris and is maintained by thoughtbot. Many improvements and bugfixes were contributed by the [open source community](https://github.com/thoughtbot/factory_bot/graphs/contributors). License ------- factory_bot is Copyright © 2008 Joe Ferris and thoughtbot. It is free software, and may be redistributed under the terms specified in the [LICENSE] file. [LICENSE]: https://github.com/thoughtbot/factory_bot/blob/main/LICENSE ## About thoughtbot ![thoughtbot](https://thoughtbot.com/thoughtbot-logo-for-readmes.svg) This repo is maintained and funded by thoughtbot, inc. The names and logos for thoughtbot are trademarks of thoughtbot, inc. We love open source software! See [our other projects][community]. We are [available for hire][hire]. [community]: https://thoughtbot.com/community?utm_source=github [hire]: https://thoughtbot.com/hire-us?utm_source=github [ci-image]: https://github.com/thoughtbot/factory_bot/actions/workflows/build.yml/badge.svg?branch=main [ci]: https://github.com/thoughtbot/factory_bot/actions?query=workflow%3ABuild+branch%3Amain [grade-image]: https://codeclimate.com/github/thoughtbot/factory_bot/badges/gpa.svg [grade]: https://codeclimate.com/github/thoughtbot/factory_bot [version-image]: https://badge.fury.io/rb/factory_bot.svg [version]: https://badge.fury.io/rb/factory_bot [hound-badge-image]: https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg [hound]: https://houndci.com factory_bot-6.5.6/RELEASING.md000066400000000000000000000046511507676517300157250ustar00rootroot00000000000000# Releasing 1. Create a release branch using the naming scheme `release-x.x.x` ```shell git checkout main git pull git checkout -b release-x.x.x ``` 2. Bump the `VERSION` inside `lib/factory_bot/version.rb` 3. Run `bundle install` to ensure the `Gemfile.lock` file is up to date. 4. Generate release notes using [GitHub's New Release](https://github.com/thoughtbot/factory_bot/releases/new) feature. 1. Click the tags drop down and select "Create new tag" 2. Fill in the create new tag modal and select "Create" Note: This is a safe step as the tag will not be created unless the release is published 3. Click the "Generate release notes" button 4. Copy the generated text from the "What's Changed" section, for use when updating `NEWS.md` in the next step. 5. Update `NEWS.md` to reflect the changes since last release. 1. Add a heading with the version number and date 2. Paste the release notes you copied from the previous step Note: A useful GitHub URL to compare the changes is: `https://github.com/thoughtbot/factory_bot/compare/vLAST_VERSION...main` 6. Commit the changes. Note: As there shouldn't be code changes, CI doesn't need to run. You can add `[ci skip]` to the commit message to skip it. 7. Create a Pull Request, get it reviewed, and merge it to the `main` branch once approved. 8. Back on your machine, switch to the `main` branch and tag the latest commit with the release version: `git tag -s vVERSION` - We recommend the [_quick guide on how to sign a release_] from git ready. 9. Push your changes: `git push && git push --tags` 10. If you haven't already, add yourself as an owner of the `factory_bot` gem on [rubygems.org](https://rubygems.org/) using [the guide in the thoughtbot handbook](https://github.com/thoughtbot/handbook/blob/main/operations/services/rubygems.md#managing-rubygems) 11. Build and publish the gem: ```bash gem build factory_bot.gemspec gem push factory_bot-VERSION.gem ``` 12. On GitHub, add a new release using the recent `NEWS.md` as the content. Sample URL: `https://github.com/thoughtbot/factory_bot/releases/new?tag=vVERSION` 13. Announce the new release, making sure to say "thank you" to the contributors who helped shape this version! thoughtbotters can refer to the handbook for announcements guidelines. [_quick guide on how to sign a release_]: http://gitready.com/advanced/2014/11/02/gpg-sign-releases.html factory_bot-6.5.6/Rakefile000066400000000000000000000016371507676517300155400ustar00rootroot00000000000000require "rubygems" require "bundler" require "rake" require "yard" require "rspec/core/rake_task" require "cucumber/rake/task" require "standard/rake" Bundler::GemHelper.install_tasks(name: "factory_bot") desc "Default: run all specs and standard" task default: %w[all_specs standard] desc "Run all specs and features" task all_specs: %w[spec:unit spec:acceptance features] namespace :spec do desc "Run unit specs" RSpec::Core::RakeTask.new("unit") do |t| t.pattern = "spec/{*_spec.rb,factory_bot/**/*_spec.rb}" end desc "Run acceptance specs" RSpec::Core::RakeTask.new("acceptance") do |t| t.pattern = "spec/acceptance/**/*_spec.rb" end end desc "Run the unit and acceptance specs" task spec: ["spec:unit", "spec:acceptance"] Cucumber::Rake::Task.new(:features) do |t| t.fork = true t.cucumber_opts = ["--format", ENV["CUCUMBER_FORMAT"] || "progress"] end YARD::Rake::YardocTask.new do |t| end factory_bot-6.5.6/SECURITY.md000066400000000000000000000012241507676517300156540ustar00rootroot00000000000000 # Security Policy ## Supported Versions Only the the latest version of this project is supported at a given time. If you find a security issue with an older version, please try updating to the latest version first. If for some reason you can't update to the latest version, please let us know your reasons so that we can have a better understanding of your situation. ## Reporting a Vulnerability For security inquiries or vulnerability reports, visit . If you have any suggestions to improve this policy, visit . factory_bot-6.5.6/bin/000077500000000000000000000000001507676517300146345ustar00rootroot00000000000000factory_bot-6.5.6/bin/console000077500000000000000000000004731507676517300162300ustar00rootroot00000000000000#!/usr/bin/env ruby # frozen_string_literal: true lib = File.expand_path("../lib", __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "bundler/setup" require "factory_bot" # Add any other fixtures and/or initialization code here. require "irb" require "irb/completion" IRB.start(__FILE__) factory_bot-6.5.6/bin/setup000077500000000000000000000001131507676517300157150ustar00rootroot00000000000000#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install factory_bot-6.5.6/cucumber.yml000066400000000000000000000000561507676517300164150ustar00rootroot00000000000000default: --publish-quiet -r features features factory_bot-6.5.6/docs/000077500000000000000000000000001507676517300150145ustar00rootroot00000000000000factory_bot-6.5.6/docs/book.toml000066400000000000000000000027201507676517300166440ustar00rootroot00000000000000[book] authors = ["Mike Burns"] language = "en" multilingual = false src = "src" title = "factory_bot" [output.html.redirect] "/setup/rspec.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-RSpec" "/setup/cucumber.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-Cucumber" "/setup/minitest-rails.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-minitest-rails" "/setup/minitest-spec.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-Minitest::Spec" "/setup/minitest.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-Minitest" "/setup/spinach.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-Spinach" "/setup/test-unit.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-Test::Unit" "/setup/update-gemfile.html" = "summary.html" "/setup/configure-test-suite.html" = "summary.html" "/using-factories/build_stubbed-and-marshaldump.html" = "build-strategies.html" "/associations/has_many-associations.html" = "../cookbook/has_many-associations.html" "/associations/has_and_belongs_to_many-associations.html" = "../cookbook/has_and_belongs_to_many-associations.html" "/associations/polymorphic-associations.html" = "../cookbook/polymorphic-associations.html" "/associations/interconnected-associations.html" = "../cookbook/interconnected-associations.html" "/traits/defining-traits.html" = "summary.html" "/callbacks/default-callbacks.html" = "summary.html" factory_bot-6.5.6/docs/src/000077500000000000000000000000001507676517300156035ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/SUMMARY.md000066400000000000000000000114201507676517300172600ustar00rootroot00000000000000# Summary [Intro](intro.md) ## Reference - [Build strategies](ref/build-strategies.md) - [FactoryBot.find_definitions](ref/find_definitions.md) - [FactoryBot.define](ref/define.md) - [factory](ref/factory.md) - [add_attribute](ref/add_attribute.md) - [association](ref/association.md) - [sequence](ref/sequence.md) - [trait](ref/trait.md) - [method_missing](ref/method_missing.md) - [traits_for_enum](ref/traits_for_enum.md) - [skip_create, to_create, and initialize_with](ref/build-and-create.md) - [transient](ref/transient.md) - [Hooks](ref/hooks.md) - [FactoryBot.modify](ref/modify.md) - [FactoryBot.lint](ref/lint.md) - [FactoryBot.register_strategy](ref/register_strategy.md) ## Guide - [Setup](setup/summary.md) - [Using Without Bundler](using-without-bundler/summary.md) - [Rails Preloaders and RSpec](rails-preloaders-and-rspec/summary.md) - [Defining factories](defining/summary.md) - [Factory name and attributes](defining/name-attributes.md) - [Specifying the class explicitly](defining/explicit-class.md) - [Definition file paths](defining/file-paths.md) - [Hash attributes](defining/hash-attributes.md) - [Best practices](defining/best-practices.md) - [Static Attributes (deprecated)](defining/static-attributes.md) - [Using factories](using-factories/summary.md) - [Build strategies](using-factories/build-strategies.md) - [Building or Creating Multiple Records](building-or-creating-multiple-records/summary.md) - [Attribute overrides](using-factories/attribute-overrides.md) - [Aliases](aliases/summary.md) - [Dependent Attributes](dependent-attributes/summary.md) - [Transient Attributes](transient-attributes/summary.md) - [With other attributes](transient-attributes/with-other-attributes.md) - [With `attributes_for`](transient-attributes/with-attributes_for.md) - [With callbacks](transient-attributes/with-callbacks.md) - [With associations](transient-attributes/with-associations.md) - [Method Name / Reserved Word Attributes](method-name-reserved-word-attributes/summary.md) - [Inheritance](inheritance/summary.md) - [Nested factories](inheritance/nested-factories.md) - [Assigning parent explicitly](inheritance/assigning-parent-explicitly.md) - [Best practices](inheritance/best-practices.md) - [Associations](associations/summary.md) - [Implicit definition](associations/implicit-definition.md) - [Explicit definition](associations/explicit-definition.md) - [Inline definition](associations/inline-definition.md) - [Specifying the factory](associations/specifying-the-factory.md) - [Overriding attributes](associations/overriding-attributes.md) - [Association overrides](associations/association-overrides.md) - [Build strategies](associations/build-strategies.md) - [Sequences](sequences/summary.md) - [Global sequences](sequences/global-sequences.md) - [With dynamic attributes](sequences/with-dynamic-attributes.md) - [As implicit attributes](sequences/as-implicit-attributes.md) - [Factory sequences](sequences/factory-sequences.md) - [Initial value](sequences/initial-value.md) - [Without a block](sequences/without-a-block.md) - [Aliases](sequences/aliases.md) - [Sequence URIs](sequences/sequence-uris.md) - [Rewinding](sequences/rewinding.md) - [Setting the value](sequences/setting-the-value.md) - [Generating a sequence](sequences/generating.md) - [Uniqueness](sequences/uniqueness.md) - [Traits](traits/summary.md) - [As implicit attributes](traits/as-implicit-attributes.md) - [Using traits](traits/using.md) - [Enum traits](traits/enum.md) - [Attribute precedence](traits/attribute-precedence.md) - [In child factories](traits/in-child-factories.md) - [As mixins](traits/mixins.md) - [With associations](traits/with-associations.md) - [Traits within traits](traits/traits-within-traits.md) - [With transient attributes](traits/with-transient-attributes.md) - [Callbacks](callbacks/summary.md) - [Multiple callbacks](callbacks/multiple-callbacks.md) - [Global callbacks](callbacks/global-callbacks.md) - [Symbol#to_proc](callbacks/symbol-to_proc.md) - [Callback order](callbacks/callback_order.md) - [Modifying factories](modifying-factories/summary.md) - [Linting Factories](linting-factories/summary.md) - [Custom Construction](custom-construction/summary.md) - [Custom Strategies](custom-strategies/summary.md) - [Custom Callbacks](custom-callbacks/summary.md) - [Custom Methods to Persist Objects](custom-methods-to-persist-objects/summary.md) - [ActiveSupport Instrumentation](activesupport-instrumentation/summary.md) ## Cookbook - [`has_many` associations](cookbook/has_many-associations.md) - [`has_and_belongs_to_many` associations](cookbook/has_and_belongs_to_many-associations.md) - [Polymorphic associations](cookbook/polymorphic-associations.md) - [Interconnected associations](cookbook/interconnected-associations.md) factory_bot-6.5.6/docs/src/activesupport-instrumentation/000077500000000000000000000000001507676517300237545ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/activesupport-instrumentation/summary.md000066400000000000000000000040141507676517300257720ustar00rootroot00000000000000# ActiveSupport Instrumentation In order to track what factories are created (and with what build strategy), `ActiveSupport::Notifications` are included to provide a way to subscribe to factories being compiled and run. One example would be to track factories based on a threshold of execution time. ```ruby ActiveSupport::Notifications.subscribe("factory_bot.run_factory") do |name, start, finish, id, payload| execution_time_in_seconds = finish - start if execution_time_in_seconds >= 0.5 $stderr.puts "Slow factory: #{payload[:name]} using strategy #{payload[:strategy]}" end end ``` Another example would be tracking all factories and how they're used throughout your test suite. If you're using RSpec, it's as simple as adding a `before(:suite)` and `after(:suite)`: ```ruby factory_bot_results = {} config.before(:suite) do ActiveSupport::Notifications.subscribe("factory_bot.run_factory") do |name, start, finish, id, payload| factory_name = payload[:name] strategy_name = payload[:strategy] factory_bot_results[factory_name] ||= {} factory_bot_results[factory_name][strategy_name] ||= 0 factory_bot_results[factory_name][strategy_name] += 1 end end config.after(:suite) do puts factory_bot_results end ``` Another example could involve tracking the attributes and traits that factories are compiled with. If you're using RSpec, you could add `before(:suite)` and `after(:suite)` blocks that subscribe to `factory_bot.compile_factory` notifications: ```ruby factory_bot_results = {} config.before(:suite) do ActiveSupport::Notifications.subscribe("factory_bot.compile_factory") do |name, start, finish, id, payload| factory_name = payload[:name] factory_class = payload[:class] attributes = payload[:attributes] traits = payload[:traits] factory_bot_results[factory_class] ||= {} factory_bot_results[factory_class][factory_name] = { attributes: attributes.map(&:name) traits: traits.map(&:name) } end end config.after(:suite) do puts factory_bot_results end ``` factory_bot-6.5.6/docs/src/aliases/000077500000000000000000000000001507676517300172245ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/aliases/summary.md000066400000000000000000000017061507676517300212470ustar00rootroot00000000000000# Aliases factory\_bot allows you to define aliases to existing factories to make them easier to re-use. This could come in handy when, for example, your Post object has an author attribute that actually refers to an instance of a User class. While normally factory\_bot can infer the factory name from the association name, in this case it will look for an author factory in vain. So, alias your user factory so it can be used under alias names. ```ruby factory :user, aliases: [:author, :commenter] do first_name { "John" } last_name { "Doe" } date_of_birth { 18.years.ago } end factory :post do # The alias allows us to write author instead of # association :author, factory: :user author title { "How to read a book effectively" } body { "There are five steps involved." } end factory :comment do # The alias allows us to write commenter instead of # association :commenter, factory: :user commenter body { "Great article!" } end ``` factory_bot-6.5.6/docs/src/associations/000077500000000000000000000000001507676517300203025ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/associations/association-overrides.md000066400000000000000000000011751507676517300251440ustar00rootroot00000000000000# Association overrides Attribute overrides can be used to link associated objects: ```ruby FactoryBot.define do factory :author do name { 'Taylor' } end factory :post do author end end eunji = build(:author, name: 'Eunji') post = build(:post, author: eunji) ``` Ruby 3.1's support for [omitting values][] from `Hash` literals dovetails with attribute overrides, and provides an opportunity to limit the repetition of variable names: ```ruby author = build(:author, name: 'Eunji') post = build(:post, author:) ``` [omitting values]: https://docs.ruby-lang.org/en/3.1/syntax/literals_rdoc.html#label-Hash+Literals factory_bot-6.5.6/docs/src/associations/build-strategies.md000066400000000000000000000031461507676517300240770ustar00rootroot00000000000000# Build strategies Associations default to using the same build strategy as their parent object: ```ruby FactoryBot.define do factory :author factory :post do author end end post = build(:post) post.new_record? # => true post.author.new_record? # => true post = create(:post) post.new_record? # => false post.author.new_record? # => false ``` This is different than the default behavior for previous versions of factory\_bot, where the association strategy would not always match the strategy of the parent object. If you want to continue using the old behavior, you can set the `use_parent_strategy` configuration option to `false`. ```ruby FactoryBot.use_parent_strategy = false # Builds and saves a User and a Post post = create(:post) post.new_record? # => false post.author.new_record? # => false # Builds and saves a User, and then builds but does not save a Post post = build(:post) post.new_record? # => true post.author.new_record? # => false ``` To not save the associated object, specify `strategy: :build` in the factory: ```ruby FactoryBot.use_parent_strategy = false factory :post do # ... association :author, factory: :user, strategy: :build end # Builds a User, and then builds a Post, but does not save either post = build(:post) post.new_record? # => true post.author.new_record? # => true ``` Note that the `strategy: :build` option must be passed to an explicit call to `association`, and cannot be used with implicit associations: ```ruby factory :post do # ... author strategy: :build # <<< this does *not* work; causes author_id to be nil ``` factory_bot-6.5.6/docs/src/associations/explicit-definition.md000066400000000000000000000003231507676517300245710ustar00rootroot00000000000000# Explicit definition You can define associations explicitly. This can be handy especially when [Overriding attributes](overriding-attributes.md) ```ruby factory :post do # ... association :author end ``` factory_bot-6.5.6/docs/src/associations/implicit-definition.md000066400000000000000000000003341507676517300245640ustar00rootroot00000000000000# Implicit definition 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. ```ruby factory :post do # ... author end ``` factory_bot-6.5.6/docs/src/associations/inline-definition.md000066400000000000000000000003631507676517300242320ustar00rootroot00000000000000# Inline definition You can also define associations inline within regular attributes, but note that the value will be `nil` when using the `attributes_for` strategy. ```ruby factory :post do # ... author { association :author } end ``` factory_bot-6.5.6/docs/src/associations/overriding-attributes.md000066400000000000000000000007131507676517300251610ustar00rootroot00000000000000# Overriding attributes You can also override attributes on associations. Implicitly: ```ruby factory :post do # ... author factory: :author, last_name: "Writely" end ``` Explicitly: ```ruby factory :post do # ... association :author, last_name: "Writely" end ``` Or inline using attributes from the factory: ```rb factory :post do # ... author_last_name { "Writely" } author { association :author, last_name: author_last_name } end ``` factory_bot-6.5.6/docs/src/associations/specifying-the-factory.md000066400000000000000000000006061507676517300252110ustar00rootroot00000000000000# Specifying the factory You can specify a different factory (although [Aliases](../aliases/summary.md) might also help you out here). Implicitly: ```ruby factory :post do # ... author factory: :user end ``` Explicitly: ```ruby factory :post do # ... association :author, factory: :user end ``` Inline: ```ruby factory :post do # ... author { association :user } end ``` factory_bot-6.5.6/docs/src/associations/summary.md000066400000000000000000000000171507676517300223170ustar00rootroot00000000000000# Associations factory_bot-6.5.6/docs/src/building-or-creating-multiple-records/000077500000000000000000000000001507676517300251005ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/building-or-creating-multiple-records/summary.md000066400000000000000000000027431507676517300271250ustar00rootroot00000000000000# Building or Creating Multiple Records Sometimes, you'll want to create or build multiple instances of a factory at once. ```ruby built_users = build_list(:user, 25) created_users = create_list(:user, 25) ``` These methods will build or create a specific amount of factories and return them as an array. To set the attributes for each of the factories, you can pass in a hash as you normally would. ```ruby twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago) ``` In order to set different attributes for each factory, these methods may be passed a block, with the factory and the index as parameters: ```ruby twenty_somethings = build_list(:user, 10) do |user, i| user.date_of_birth = (20 + i).years.ago end ``` `create_list` passes saved instances into the block. If you modify the instance, you must save it again: ```ruby twenty_somethings = create_list(:user, 10) do |user, i| user.date_of_birth = (20 + i).years.ago user.save! end ``` `build_stubbed_list` will give you fully stubbed out instances: ```ruby stubbed_users = build_stubbed_list(:user, 25) # array of stubbed users ``` There's also a set of `*_pair` methods for creating two records at a time: ```ruby built_users = build_pair(:user) # array of two built users created_users = create_pair(:user) # array of two created users ``` If you need multiple attribute hashes, `attributes_for_list` will generate them: ```ruby users_attrs = attributes_for_list(:user, 25) # array of attribute hashes ``` factory_bot-6.5.6/docs/src/callbacks/000077500000000000000000000000001507676517300175225ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/callbacks/callback_order.md000066400000000000000000000060571507676517300230030ustar00rootroot00000000000000# Callback Order When a callback event like `after_build` or `before_all` is triggered, all callbacks for that event are executed in the following order: 1. Global callbacks. 2. Inherited callbacks. 3. Factory callbacks. 4. Trait callbacks (in the order requested). ## A simple factory example: ```ruby FactoryBot.define do before(:all) { puts "Global before(:all)" } after(:all) { puts "Global after(:all)" } factory :user do before(:all) { puts "User before(:all)" } after(:all) { puts "User after(:all)" } before(:build) { puts "User before(:build)" } after(:build) { puts "User after(:build)" } trait :trait_a do before(:build) { puts "Trait-A before(:build)" } after(:build) { puts "Trait-A after(:build)" } end trait :trait_b do before(:build) { puts "Trait-B before(:build)" } after(:build) { puts "Trait-B after(:build)" } end end end build(:user, :trait_b, :trait_a) # Result: # # 1. "Global before(:all)" # 2. "User before(:all)" # 3. "User before(:build) # 4. "Trait-B before(:build)" # 5. "Trait-A before(:build)" # 6. "User after(:build)" # 7. "Trait-B after(:build)" # 8. "Trait-A after(:build)" # 9. "Global after(:all)" # 10. "User after(:all)" ``` ## An inherited factory example: ```ruby FactoryBot.define do before(:all) { puts "Global before(:all)" } before(:build) { puts "Global before(:build)" } after(:build) { puts "Global after(:build)" } after(:all) { puts "Global after(:all)" } factory :parent do before(:all) { puts "Parent before(:all)" } before(:build) { puts "Parent before(:build)" } after(:all) { puts "Parent after(:all)" } after(:build) { puts "Parent after(:build)" } trait :trait_a do before(:build) { puts "Trait-A before(:build)" } after(:build) { puts "Trait-A after(:build)" } end factory :child do before(:all) { puts "Child before(:all)" } before(:build) { puts "Child before(:build)" } after(:build) { puts "Child after(:build)" } after(:all) { puts "Child after(:all)" } trait :trait_b do before(:build) { puts "Trait-B before(:build)" } after(:build) { puts "Trait-B after(:build)" } after(:all) { puts "Trait-B after(:all)" } end trait :trait_c do before(:build) { puts "Trait-C before(:build)" } after(:build) { puts "Trait-C after(:build)" } before(:all) { puts "Trait-C before(:all)" } end end end end build(:child, :trait_c, :trait_a, :trait_b) # Result: # # 1. "Global before(:all)" # 2. "Parent before(:all)" # 3. "Child before(:all)" # 4. "Trait-C before(:all)" # 5. "Global before(:build)" # 6. "Parent before(:build)" # 7. "Child before(:build)" # 8. "Trait-C before(:build)" # 9. "Trait-A before(:build)" # 10. "Trait-B before(:build)" # 11. "Global after(:build)" # 12. "Parent after(:build)" # 13. "Child after(:build)" # 14. "Trait-C after(:build)" # 15. "Trait-A after(:build)" # 16. "Trait-B after(:build)" # 17. "Global after(:all)" # 18. "Parent after(:all)" # 19. "Child after(:all)" # 20. "Trait-B after(:all)" ``` factory_bot-6.5.6/docs/src/callbacks/default-callbacks.md000066400000000000000000000020631507676517300234060ustar00rootroot00000000000000# Default callbacks factory\_bot makes available four callbacks for injecting some code: * before(:all) - called before any strategy is used (e.g., `FactoryBot.build`, `FactoryBot.create`, `FactoryBot.build_stubbed`) * before(:build) - called before a factory is built (via `FactoryBot.build`, `FactoryBot.create`) * after(:build) - called after a factory is built (via `FactoryBot.build`, `FactoryBot.create`) * before(:create) - called before a factory is saved (via `FactoryBot.create`) * after(:create) - called after a factory is saved (via `FactoryBot.create`) * after(:stub) - called after a factory is stubbed (via `FactoryBot.build_stubbed`) * after(:all) - called after any strategy is used (e.g., `FactoryBot.build`, `FactoryBot.create`, `FactoryBot.build_stubbed`) Examples: ```ruby # Define a factory that calls the generate_hashed_password method after it is built factory :user do after(:build) { |user| generate_hashed_password(user) } end ``` Note that you'll have an instance of the object in the block. This can be useful. factory_bot-6.5.6/docs/src/callbacks/global-callbacks.md000066400000000000000000000005031507676517300232170ustar00rootroot00000000000000# Global callbacks To override callbacks for all factories, define them within the `FactoryBot.define` block: ```ruby FactoryBot.define do after(:build) { |object| puts "Built #{object}" } after(:create) { |object| AuditLog.create(attrs: object.attributes) } factory :user do name { "John Doe" } end end ``` factory_bot-6.5.6/docs/src/callbacks/multiple-callbacks.md000066400000000000000000000020251507676517300236130ustar00rootroot00000000000000# Multiple callbacks You can also define multiple types of callbacks on the same factory: ```ruby factory :user do after(:build) { |user| do_something_to(user) } after(:create) { |user| do_something_else_to(user) } end ``` Factories can also define any number of the same kind of callback. These callbacks will be executed in the order they are specified: ```ruby factory :user do after(:create) { this_runs_first } after(:create) { then_this } end ``` Calling `create` will invoke both `after_build` and `after_create` callbacks. Also, like standard attributes, child factories will inherit (and can also define) callbacks from their parent factory. Multiple callbacks can be assigned to run a block; this is useful when building various strategies that run the same code (since there are no callbacks that are shared across all strategies). ```ruby factory :user do callback(:after_stub, :before_create) { do_something } after(:stub, :create) { do_something_else } before(:create, :custom) { do_a_third_thing } end ``` factory_bot-6.5.6/docs/src/callbacks/summary.md000066400000000000000000000037721507676517300215520ustar00rootroot00000000000000# Callbacks factory\_bot makes six callbacks available: | Callback | Timing | | --------------- | ------------------------------------------------------------------------------------------------------------------------- | | before(:all) | called before any strategy is used to construct an object, including custom strategies | | before(:build) | called before a factory builds an object (via `FactoryBot.build` or `FactoryBot.create`) | | after(:build) | called after a factory builds an object (via `FactoryBot.build` or `FactoryBot.create`) | | before(:create) | called before a factory saves an object (via `FactoryBot.create`) | | after(:create) | called after a factory saves an object (via `FactoryBot.create`) | | after(:stub) | called after a factory stubs an object (via `FactoryBot.build_stubbed`) | | after(:all) | called after any strategy has completed, including custom strategies | ## Examples ### Calling an object's own method after building ```ruby ## # Define a factory that calls the generate_hashed_password method # after the user factory is built. # # Note that you'll have an instance of the object in the block # factory :user do after(:build) { |user, context| generate_hashed_password(user) } end ``` ### Skipping an object's own :after_create callback ```ruby ## # Disable a model's own :after_create callback that sends an email # on creation, then re-enable it afterwards # factory :user do before(:all){ User.skip_callback(:create, :after, :send_welcome_email) } after(:all){ User.set_callback(:create, :after, :send_welcome_email) } end ``` factory_bot-6.5.6/docs/src/callbacks/symbol-to_proc.md000066400000000000000000000005311507676517300230130ustar00rootroot00000000000000# Symbol#to_proc You can call callbacks that rely on `Symbol#to_proc`: ```ruby # app/models/user.rb class User < ActiveRecord::Base def confirm! # confirm the user account end end # spec/factories.rb FactoryBot.define do factory :user do after :create, &:confirm! end end create(:user) # creates the user and confirms it ``` factory_bot-6.5.6/docs/src/cookbook/000077500000000000000000000000001507676517300174115ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/cookbook/has_and_belongs_to_many-associations.md000066400000000000000000000021401507676517300272610ustar00rootroot00000000000000# has_and_belongs_to_many associations Generating data for a `has_and_belongs_to_many` relationship is very similar to the above `has_many` relationship, with a small change: you need to pass an array of objects to the model's pluralized attribute name rather than a single object to the singular version of the attribute name. ```ruby def profile_with_languages(languages_count: 2) FactoryBot.create(:profile) do |profile| FactoryBot.create_list(:language, languages_count, profiles: [profile]) end end ``` Or with the callback approach: ```ruby factory :profile_with_languages do transient do languages_count { 2 } end after(:create) do |profile, context| create_list(:language, context.languages_count, profiles: [profile]) profile.reload end end ``` Or the inline association approach (note the use of the `instance` method here to refer to the profile being built): ```ruby factory :profile_with_languages do transient do languages_count { 2 } end languages do Array.new(languages_count) do association(:language, profiles: [instance]) end end end ``` factory_bot-6.5.6/docs/src/cookbook/has_many-associations.md000066400000000000000000000062731507676517300242370ustar00rootroot00000000000000# has_many associations There are a few ways to generate data for a `has_many` relationship. The simplest approach is to write a helper method in plain Ruby to tie together the different records: ```ruby FactoryBot.define do factory :post do title { "Through the Looking Glass" } user end factory :user do name { "Rachel Sanchez" } end end def user_with_posts(posts_count: 5) FactoryBot.create(:user) do |user| FactoryBot.create_list(:post, posts_count, user: user) end end create(:user).posts.length # 0 user_with_posts.posts.length # 5 user_with_posts(posts_count: 15).posts.length # 15 ``` If you prefer to keep the object creation fully within factory\_bot, you can build the posts in an `after(:create)` callback. ```ruby FactoryBot.define do factory :post do title { "Through the Looking Glass" } user end factory :user do name { "John Doe" } # user_with_posts will create post data after the user has been created factory :user_with_posts do # posts_count is declared as a transient attribute available in the # callback via the context transient do posts_count { 5 } end # the after(:create) yields two values; the user instance itself and the # context, which stores all values from the factory, including transient # attributes; `create_list`'s second argument is the number of records # to create and we make sure the user is associated properly to the post after(:create) do |user, context| create_list(:post, context.posts_count, user: user) # You may need to reload the record here, depending on your application user.reload end end end end create(:user).posts.length # 0 create(:user_with_posts).posts.length # 5 create(:user_with_posts, posts_count: 15).posts.length # 15 ``` The following is a simple example that works without having to save to the database. It works with `build`, `build_stubbed`, and `create` (although it doesn't work well with `attributes_for`), you can use inline associations: ```ruby FactoryBot.define do factory :post do title { "Through the Looking Glass" } user end factory :user do name { "Taylor Kim" } factory :user_with_posts do posts { [association(:post)] } end # or trait :with_posts do posts { [association(:post)] } end end end create(:user).posts.length # 0 create(:user_with_posts).posts.length # 1 build(:user_with_posts).posts.length # 1 build_stubbed(:user_with_posts).posts.length # 1 ``` For more flexibility you can combine this with the `posts_count` transient attribute from the callback example: ```ruby FactoryBot.define do factory :post do title { "Through the Looking Glass" } user end factory :user do name { "Adiza Kumato" } factory :user_with_posts do transient do posts_count { 5 } end posts do Array.new(posts_count) { association(:post) } end end end end create(:user_with_posts).posts.length # 5 create(:user_with_posts, posts_count: 15).posts.length # 15 build(:user_with_posts, posts_count: 15).posts.length # 15 build_stubbed(:user_with_posts, posts_count: 15).posts.length # 15 ``` factory_bot-6.5.6/docs/src/cookbook/interconnected-associations.md000066400000000000000000000031021507676517300254300ustar00rootroot00000000000000# Interconnected associations There are limitless ways objects might be interconnected, and factory\_bot may not always be suited to handle those relationships. In some cases it makes sense to use factory\_bot to build each individual object, and then to write helper methods in plain Ruby to tie those objects together. That said, some more complex, interconnected relationships can be built in factory\_bot using inline associations with reference to the `instance` being built. Let's say your models look like this, where an associated `Student` and `Profile` should both belong to the same `School`: ```ruby class Student < ApplicationRecord belongs_to :school has_one :profile end class Profile < ApplicationRecord belongs_to :school belongs_to :student end class School < ApplicationRecord has_many :students has_many :profiles end ``` We can ensure the student and profile are connected to each other and to the same school with a factory like this: ```ruby FactoryBot.define do factory :student do school profile { association :profile, student: instance, school: school } end factory :profile do school student { association :student, profile: instance, school: school } end factory :school end ``` Note that this approach works with `build`, `build_stubbed`, and `create`, but the associations will return `nil` when using `attributes_for`. Also, note that if you assign any attributes inside a custom `initialize_with` (e.g. `initialize_with { new(**attributes) }`), those attributes should not refer to `instance`, since it will be `nil`. factory_bot-6.5.6/docs/src/cookbook/polymorphic-associations.md000066400000000000000000000010141507676517300247710ustar00rootroot00000000000000# Polymorphic associations Polymorphic associations can be handled with traits: ```ruby FactoryBot.define do factory :video factory :photo factory :comment do for_photo # default to the :for_photo trait if none is specified trait :for_video do association :commentable, factory: :video end trait :for_photo do association :commentable, factory: :photo end end end ``` This allows us to do: ```ruby create(:comment) create(:comment, :for_video) create(:comment, :for_photo) ``` factory_bot-6.5.6/docs/src/custom-callbacks/000077500000000000000000000000001507676517300210325ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/custom-callbacks/summary.md000066400000000000000000000015171507676517300230550ustar00rootroot00000000000000# Custom Callbacks Custom callbacks can be defined if you're using custom strategies: ```ruby class JsonStrategy def initialize @strategy = FactoryBot.strategy_by_name(:create).new end delegate :association, to: :@strategy def result(evaluation) result = @strategy.result(evaluation) evaluation.notify(:before_json, result) result.to_json.tap do |json| evaluation.notify(:after_json, json) evaluation.notify(:make_json_awesome, json) end end def to_sym :json end end FactoryBot.register_strategy(:json, JsonStrategy) FactoryBot.define do factory :user do before(:json) { |user| do_something_to(user) } after(:json) { |user_json| do_something_to(user_json) } callback(:make_json_awesome) { |user_json| do_something_to(user_json) } end end ``` factory_bot-6.5.6/docs/src/custom-construction/000077500000000000000000000000001507676517300216455ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/custom-construction/summary.md000066400000000000000000000054341507676517300236720ustar00rootroot00000000000000# Custom Construction If you want to use factory\_bot to construct an object where some attributes are passed to `initialize` or if you want to do something other than simply calling `new` on your build class, you can override the default behavior by defining `initialize_with` on your factory. Example: ```ruby # user.rb class User attr_accessor :name, :email def initialize(name) @name = name end end # factories.rb sequence(:email) { |n| "person#{n}@example.com" } factory :user do name { "Jane Doe" } email initialize_with { new(name) } end build(:user).name # Jane Doe ``` Although factory\_bot is written to work with ActiveRecord out of the box, it can also work with any Ruby class. For maximum compatibility with ActiveRecord, the default initializer builds all instances by calling `new` on your build class without any arguments. It then calls attribute writer methods to assign all the attribute values. While that works fine for ActiveRecord, it actually doesn't work for almost any other Ruby class. You can override the initializer in order to: * Build non-ActiveRecord objects that require arguments to `initialize` * Use a method other than `new` to instantiate the instance * Do wild things like decorate the instance after it's built When using `initialize_with`, you don't have to declare the class itself when calling `new`; however, any other class methods you want to call will have to be called on the class explicitly. For example: ```ruby factory :user do name { "John Doe" } initialize_with { User.build_with_name(name) } end ``` You can also access all public attributes within the `initialize_with` block by calling `attributes`: ```ruby factory :user do transient do comments_count { 5 } end name "John Doe" initialize_with { new(**attributes) } end ``` This will build a hash of all attributes to be passed to `new`. It won't include transient attributes, but everything else defined in the factory will be passed (associations, evaluated sequences, etc.) You can define `initialize_with` for all factories by including it in the `FactoryBot.define` block: ```ruby FactoryBot.define do initialize_with { new("Awesome first argument") } end ``` When using `initialize_with`, attributes accessed from within the `initialize_with` block are assigned *only* in the constructor; this equates to roughly the following code: ```ruby FactoryBot.define do factory :user do initialize_with { new(name) } name { 'value' } end end build(:user) # runs User.new('value') ``` This prevents duplicate assignment; in versions of factory\_bot before 4.0, it would run this: ```ruby FactoryBot.define do factory :user do initialize_with { new(name) } name { 'value' } end end build(:user) # runs user = User.new('value') user.name = 'value' ``` factory_bot-6.5.6/docs/src/custom-methods-to-persist-objects/000077500000000000000000000000001507676517300243145ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/custom-methods-to-persist-objects/summary.md000066400000000000000000000012631507676517300263350ustar00rootroot00000000000000# Custom Methods to Persist Objects By default, creating a record will call `save!` on the instance; since this may not always be ideal, you can override that behavior by defining `to_create` on the factory: ```ruby factory :different_orm_model do to_create { |instance| instance.persist! } end ``` To disable the persistence method altogether on create, you can `skip_create` for that factory: ```ruby factory :user_without_database do skip_create end ``` To override `to_create` for all factories, define it within the `FactoryBot.define` block: ```ruby FactoryBot.define do to_create { |instance| instance.persist! } factory :user do name { "John Doe" } end end ``` factory_bot-6.5.6/docs/src/custom-strategies/000077500000000000000000000000001507676517300212655ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/custom-strategies/summary.md000066400000000000000000000027111507676517300233050ustar00rootroot00000000000000# Custom Strategies There are times where you may want to extend behavior of factory\_bot by adding a custom build strategy. Strategies define two methods: `association` and `result`. `association` receives a `FactoryBot::FactoryRunner` instance, upon which you can call `run`, overriding the strategy if you want. The second method, `result`, receives a `FactoryBot::Evaluation` instance. It provides a way to trigger callbacks (with `notify`), `object` or `hash` (to get the result instance or a hash based on the attributes defined in the factory), and `create`, which executes the `to_create` callback defined on the factory. To understand how factory\_bot uses strategies internally, it's probably easiest to view the source for each of the four default strategies. Here's an example of composing a strategy using `FactoryBot::Strategy::Create` to build a JSON representation of your model. ```ruby class JsonStrategy def initialize @strategy = FactoryBot.strategy_by_name(:create).new end delegate :association, to: :@strategy def result(evaluation) @strategy.result(evaluation).to_json end def to_sym :json end end ``` For factory\_bot to recognize the new strategy, you can register it: ```ruby FactoryBot.register_strategy(:json, JsonStrategy) ``` This allows you to call ```ruby FactoryBot.json(:user) ``` Finally, you can override factory\_bot's own strategies if you'd like by registering a new object in place of the strategies. factory_bot-6.5.6/docs/src/defining/000077500000000000000000000000001507676517300173665ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/defining/best-practices.md000066400000000000000000000007701507676517300226240ustar00rootroot00000000000000# Best practices It is recommended that you have one factory for each class that provides the simplest set of attributes necessary to create an instance of that class. If you're creating ActiveRecord objects, that means that you should only provide attributes that are required through validations and that do not have defaults. Other factories can be created through inheritance to cover common scenarios for each class. Attempting to define multiple factories with the same name will raise an error. factory_bot-6.5.6/docs/src/defining/explicit-class.md000066400000000000000000000007211507676517300226340ustar00rootroot00000000000000# Specifying the class explicitly It is also possible to explicitly specify the class: ```ruby # This will use the User class (otherwise Admin would have been guessed) factory :admin, class: "User" ``` You can pass a constant as well, if the constant is available (note that this can cause test performance problems in large Rails applications, since referring to the constant will cause it to be eagerly loaded). ```ruby factory :access_token, class: User ``` factory_bot-6.5.6/docs/src/defining/file-paths.md000066400000000000000000000005211507676517300217420ustar00rootroot00000000000000# Definition file paths Factories can be defined anywhere, but will be automatically loaded after calling `FactoryBot.find_definitions` if factories are defined in files at the following locations: factories.rb factories/**/*.rb test/factories.rb test/factories/**/*.rb spec/factories.rb spec/factories/**/*.rb factory_bot-6.5.6/docs/src/defining/hash-attributes.md000066400000000000000000000016421507676517300230220ustar00rootroot00000000000000# Hash attributes Because of the block syntax in Ruby, defining attributes as `Hash`es (for serialized/JSON columns, for example) requires two sets of curly brackets: ```ruby factory :program do configuration { { auto_resolve: false, auto_define: true } } end ``` Alternatively you may prefer `do`/`end` syntax: ```ruby factory :program do configuration do { auto_resolve: false, auto_define: true } end end ``` --- However, defining a value as a hash makes it complicated to set values within the hash when constructing an object. Instead, prefer to use factory\_bot itself: ```ruby factory :program do configuration { attributes_for(:configuration) } end factory :configuration do auto_resolve { false } auto_define { true } end ``` This way you can more easily set value when building: ```ruby create( :program, configuration: attributes_for( :configuration, auto_resolve: true, ) ) ``` factory_bot-6.5.6/docs/src/defining/name-attributes.md000066400000000000000000000004651507676517300230210ustar00rootroot00000000000000# Factory name and attributes Each factory has a name and a set of attributes. The name is used to guess the class of the object by default: ```ruby # This will guess the User class FactoryBot.define do factory :user do first_name { "John" } last_name { "Doe" } admin { false } end end ``` factory_bot-6.5.6/docs/src/defining/static-attributes.md000066400000000000000000000003761507676517300233710ustar00rootroot00000000000000# Static Attributes Static attributes (without a block) are no longer available in factory\_bot 5. You can read more about the decision to remove them in [this blog post](https://robots.thoughtbot.com/deprecating-static-attributes-in-factory_bot-4-11). factory_bot-6.5.6/docs/src/defining/summary.md000066400000000000000000000000251507676517300214020ustar00rootroot00000000000000# Defining factories factory_bot-6.5.6/docs/src/dependent-attributes/000077500000000000000000000000001507676517300217355ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/dependent-attributes/summary.md000066400000000000000000000005421507676517300237550ustar00rootroot00000000000000# Dependent Attributes Attributes can be based on the values of other attributes using the context that is yielded to dynamic attribute blocks: ```ruby factory :user do first_name { "Joe" } last_name { "Blow" } email { "#{first_name}.#{last_name}@example.com".downcase } end create(:user, last_name: "Doe").email # => "joe.doe@example.com" ``` factory_bot-6.5.6/docs/src/inheritance/000077500000000000000000000000001507676517300200745ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/inheritance/assigning-parent-explicitly.md000066400000000000000000000003041507676517300260500ustar00rootroot00000000000000# Assigning parent explicitly You can also assign the parent explicitly: ```ruby factory :post do title { "A title" } end factory :approved_post, parent: :post do approved { true } end ``` factory_bot-6.5.6/docs/src/inheritance/best-practices.md000066400000000000000000000003341507676517300233260ustar00rootroot00000000000000# Best practices As mentioned above, it's good practice to define a basic factory for each class with only the attributes required to create it. Then, create more specific factories that inherit from this basic parent. factory_bot-6.5.6/docs/src/inheritance/nested-factories.md000066400000000000000000000005431507676517300236570ustar00rootroot00000000000000# Nested factories You can create multiple factories for the same class without repeating common attributes by nesting factories: ```ruby factory :post do title { "A title" } factory :approved_post do approved { true } end end approved_post = create(:approved_post) approved_post.title # => "A title" approved_post.approved # => true ``` factory_bot-6.5.6/docs/src/inheritance/summary.md000066400000000000000000000000161507676517300221100ustar00rootroot00000000000000# Inheritance factory_bot-6.5.6/docs/src/intro.md000066400000000000000000000026111507676517300172600ustar00rootroot00000000000000# Intro factory_bot is a fixtures replacement with a straightforward definition syntax, support for multiple build strategies (saved instances, unsaved instances, attribute hashes, and stubbed objects), and support for multiple factories for the same class (user, admin_user, and so on), including factory inheritance. Its documentation is split as such: - the [guide](setup/summary.html) is a great place to start for first-time users. - the [cookbook](cookbook/has_many-associations.html) is the go-to place for creative solutions to common situations. - the [wiki](https://github.com/thoughtbot/factory_bot/wiki) details considerations for integrating with other software. - the [reference](ref/build-strategies.html) is terse facts for those who use this project often. ## License factory_bot is Copyright © 2008 Joe Ferris and thoughtbot. It is free software, and may be redistributed under the terms specified in the [LICENSE] file. [LICENSE]: https://github.com/thoughtbot/factory_bot/blob/main/LICENSE ## About thoughtbot factory_bot is maintained and funded by thoughtbot, inc. The names and logos for thoughtbot are trademarks of thoughtbot, inc. We love open source software! See [our other projects][community] or [hire us][hire] to design, develop, and grow your product. [community]: https://thoughtbot.com/community?utm_source=github [hire]: https://thoughtbot.com/hire-us?utm_source=github factory_bot-6.5.6/docs/src/linting-factories/000077500000000000000000000000001507676517300212245ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/linting-factories/summary.md000066400000000000000000000041011507676517300232370ustar00rootroot00000000000000# Linting Factories factory\_bot allows for linting known factories: ```ruby FactoryBot.lint ``` `FactoryBot.lint` creates each factory and catches any exceptions raised during the creation process. `FactoryBot::InvalidFactoryError` is raised with a list of factories (and corresponding exceptions) for factories which could not be created. Recommended usage of `FactoryBot.lint` is to run this in a separate task before your test suite is executed. Running it in a `before(:suite)` will negatively impact the performance of your tests when running single tests. Example Rake task: ```ruby # lib/tasks/factory_bot.rake namespace :factory_bot do desc "Verify that all FactoryBot factories are valid" task lint: :environment do if Rails.env.test? conn = ActiveRecord::Base.connection conn.transaction do FactoryBot.lint raise ActiveRecord::Rollback end else system("bundle exec rake factory_bot:lint RAILS_ENV='test'") fail if $?.exitstatus.nonzero? end end end ``` After calling `FactoryBot.lint`, you'll likely want to clear out the database, as records will most likely be created. The provided example above uses an SQL transaction and rollback to leave the database clean. You can lint factories selectively by passing only factories you want linted: ```ruby factories_to_lint = FactoryBot.factories.reject do |factory| factory.name =~ /^old_/ end FactoryBot.lint factories_to_lint ``` This would lint all factories that aren't prefixed with `old_`. Traits can also be linted. This option verifies that each and every trait of a factory generates a valid object on its own. This is turned on by passing `traits: true` to the `lint` method: ```ruby FactoryBot.lint traits: true ``` This can also be combined with other arguments: ```ruby FactoryBot.lint factories_to_lint, traits: true ``` You can also specify the strategy used for linting: ```ruby FactoryBot.lint strategy: :build ``` Verbose linting will include full backtraces for each error, which can be helpful for debugging: ```ruby FactoryBot.lint verbose: true ``` factory_bot-6.5.6/docs/src/method-name-reserved-word-attributes/000077500000000000000000000000001507676517300247535ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/method-name-reserved-word-attributes/summary.md000066400000000000000000000006471507676517300270010ustar00rootroot00000000000000# Method Name / Reserved Word Attributes If your attributes conflict with existing methods or reserved words (all methods in the [DefinitionProxy](https://github.com/thoughtbot/factory_bot/blob/main/lib/factory_bot/definition_proxy.rb) class) you can define them with `add_attribute`. ```ruby factory :dna do add_attribute(:sequence) { 'GATTACA' } end factory :payment do add_attribute(:method) { 'paypal' } end ``` factory_bot-6.5.6/docs/src/modifying-factories/000077500000000000000000000000001507676517300215455ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/modifying-factories/summary.md000066400000000000000000000025511507676517300235670ustar00rootroot00000000000000# Modifying factories If you're given a set of factories (say, from a gem developer) but want to change them to fit into your application better, you can modify that factory instead of creating a child factory and adding attributes there. If a gem were to give you a User factory: ```ruby FactoryBot.define do factory :user do full_name { "John Doe" } sequence(:username) { |n| "user#{n}" } password { "password" } end end ``` Instead of creating a child factory that added additional attributes: ```ruby FactoryBot.define do factory :application_user, parent: :user do full_name { "Jane Doe" } date_of_birth { 21.years.ago } health { 90 } end end ``` You could modify that factory instead. ```ruby FactoryBot.modify do factory :user do full_name { "Jane Doe" } date_of_birth { 21.years.ago } health { 90 } end end ``` When modifying a factory, you can change any of the attributes you want (aside from callbacks). `FactoryBot.modify` must be called outside of a `FactoryBot.define` block as it operates on factories differently. A caveat: you can only modify factories (not sequences or traits), and callbacks *still compound as they normally would*. So, if the factory you're modifying defines an `after(:create)` callback, you defining an `after(:create)` won't override it, it will instead be run after the first callback. factory_bot-6.5.6/docs/src/rails-preloaders-and-rspec/000077500000000000000000000000001507676517300227255ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/rails-preloaders-and-rspec/summary.md000066400000000000000000000017031507676517300247450ustar00rootroot00000000000000# Rails Preloaders and RSpec When running RSpec with a Rails preloader such as `spring` or `zeus`, it's possible to encounter an `ActiveRecord::AssociationTypeMismatch` error when creating a factory with associations, as below: ```ruby FactoryBot.define do factory :united_states, class: "Location" do name { 'United States' } association :location_group, factory: :north_america end factory :north_america, class: "LocationGroup" do name { 'North America' } end end ``` The error occurs during the run of the test suite: ``` Failure/Error: united_states = create(:united_states) ActiveRecord::AssociationTypeMismatch: LocationGroup(#70251250797320) expected, got LocationGroup(#70251200725840) ``` The two possible solutions are to either run the suite without the preloader, or to add `FactoryBot.reload` to the RSpec configuration, like so: ```ruby RSpec.configure do |config| config.before(:suite) { FactoryBot.reload } end ``` factory_bot-6.5.6/docs/src/ref/000077500000000000000000000000001507676517300163575ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/ref/add_attribute.md000066400000000000000000000012751507676517300215210ustar00rootroot00000000000000# add_attribute Within a factory definition, the `add_attribute` method defines a key/value pair that will be set when the object is built. The `add_attribute` method takes two arguments: a name (Symbol or String) and a block. This block is called each time this object is constructed. The block is not called when the attribute is overridden by a build strategy. Assignment is done by calling the Ruby attribute setter. For example, given ```ruby FactoryBot.define do factory :user do add_attribute(:name) { "Acid Burn" } end end ``` This will use the `#name=` setter: ```ruby user = User.new user.name = "Acid Burn" ``` Also see [method_missing](method_missing.html) for a shorthand. factory_bot-6.5.6/docs/src/ref/association.md000066400000000000000000000011571507676517300212210ustar00rootroot00000000000000# association Within a factory block, use the `association` method to always make an additional object alongside this one. This name best makes sense within the context of ActiveRecord. The `association` method takes a mandatory name and optional options. The options are zero or more trait names (Symbols), followed by a hash of attribute overrides. When constructing this association, factory\_bot uses the trait and attribute overrides given. See [method_missing](method_missing.html) for a shorthand. See [build strategies](build-strategies.html) for an explanation of how each build strategy handles associations. factory_bot-6.5.6/docs/src/ref/build-and-create.md000066400000000000000000000023041507676517300220000ustar00rootroot00000000000000# skip_create, to_create, and initialize_with The `skip_create`, `to_create`, and `initialize_with` methods control how factory\_bot interacts with the [build strategies](build-strategies.html). These methods can be called within a `factory` definition block, to scope their effects to just that factory; or within `FactoryBot.define`, to affect global change. ## initialize_with The `initialize_with` method takes a block and returns an instance of the factory's class. It has access to the `attributes` method, which is a hash of all the fields and values for the object. The default definition is: ```ruby initialize_with { new } ``` ## to_create The `to_create` method lets you control the `FactoryBot.create` strategy. This method takes a block which takes the object as constructed by `initialize_with`, and the factory\_bot context. The context has additional data from any [`transient`] blocks. [`transient`]: transient.html The default definition is: ```ruby to_create { |obj, context| obj.save! } ``` The `skip_create` method is a shorthand for turning `to_create` into a no-op. This allows you to use the `create` strategy as a synonym for `build`, except you additionally get any `create` hooks. factory_bot-6.5.6/docs/src/ref/build-strategies.md000066400000000000000000000070111507676517300221470ustar00rootroot00000000000000# Build strategies Once a factory\_bot factory is defined, it can be constructed using any of the built-in build strategies, or a custom build strategy. All of these strategies notify on the `factory_bot.run_factory` instrumentation using [ActiveSupport::Notifications], passing a payload with `:name`, `:strategy`, `:traits`, `:overrides`, and `:factory` keys. [ActiveSupport::Notifications]: https://api.rubyonrails.org/classes/ActiveSupport/Notifications.html The non-list (`.build`, `.build_pair`, `.create`, etc.) methods take one mandatory argument: the name of the factory. They can then optionally take names of traits, and then a hash of attributes to override. Finally, they can take a block. This block takes the produced object as an argument, and returns an updated object. The list methods (`.build_list`, `.create_list`, etc.) have two required arguments: the name of the factory, and the number of instances to build. They then can optionally take traits and overrides. Finally, they can take a block. This block takes the produced object and the zero-based index as arguments, and returns an updated object. ## `build` The `FactoryBot.build` method constructs an instance of the class according to `initialize_with`, which defaults to calling the `.new` class method. `.build_list` constructs multiple instances, and `.build_pair` is a shorthand to construct two instances. After it calls `initialize_with`, it invokes the `after_build` hook. Associations are constructed using the `build` build strategy. ## `create` The `FactoryBot.create` method constructs an instance of the class according to `initialize_with`, and then persists it using `to_create`. The `.create_list` class method constructs multiple instances, and `.create_pair` is a shorthand to construct two instances. After it calls `initialize_with`, it invokes the following hooks in order: 1. `after_build` 1. `before_create` 1. non-hook: `to_create` 1. `after_create` Associations are constructed using the `create` build strategy. The `to_create` hook controls how objects are persisted. It takes a block with the object and the factory\_bot context, and runs it for its side effect. By default, it calls `#save!`. ## `attributes_for` The `FactoryBot.attributes_for` method constructs a Hash with the attributes and their values, using `initialize_with`. The `attributes_for_pair` and `attributes_for_list` methods work similarly as `build_pair` and `build_list`. Associations are constructed using the `null` build strategy (they are not built). No hooks are called. ## `build_stubbed` The `FactoryBot.build_stubbed` method returns a fake ActiveRecord object. The `.build_stubbed_pair` and `.build_stubbed_list` methods are defined similarly to `.build_pair` and `.build_list`. It uses `initialize_with` to construct the object, but then stubs methods and data as appropriate: - `id` is set sequentially (unless overridden by attributes) - `created_at` and `updated_at` are set to the current time (unless overridden by attributes) - all [ActiveModel::Dirty] change tracking is cleared - `persisted?` is true - `new_record?` is false - `destroyed?` is false - persistence methods raise a `RuntimeError` (`#connection`, `#delete`, `#save`, `#update`, etc.) [ActiveModel::Dirty]: https://api.rubyonrails.org/classes/ActiveModel/Dirty.html After it sets up the object it invokes the `after_stub` hook. ## `null` The `FactoryBot.null` method returns `nil`. The `.null_pair` method gives you a pair of nils, and `.null_list` gives as many nils as you desire. This is used internally. factory_bot-6.5.6/docs/src/ref/define.md000066400000000000000000000004011507676517300201260ustar00rootroot00000000000000# FactoryBot.define Each file loaded by factory\_bot is expected to call `FactoryBot.define` with a block. The block is evaluated within an instance of `FactoryBot::Syntax::Default::DSL`, giving access to `factory`, `sequence`, `trait`, and other methods. factory_bot-6.5.6/docs/src/ref/factory.md000066400000000000000000000031061507676517300203500ustar00rootroot00000000000000# factory Within a `FactoryBot.define` block, you can define factories. Anything defined using `factory` can be built using a [build strategy](build-strategies.html). The `factory` method takes three arguments: a required name, an optional hash of options, and an optional block. The name is expected to be a Symbol. ## Options - `:class` - what class to construct. This can be either a class, or a String or Symbol (anything that responds to `#to_s`). By default it is either the parent's class name or the factory's name. - `:parent`- the name of another factory that this factory should inherit from. Defaults to `nil`. - `:aliases` - alternative names for this factory. Any of these names can be used with a build strategy. Defaults to the empty list. - `:traits` - base traits that are used by default when building this factory. Defaults to the empty list. ## Block You can use the block to define your factory. Within here you have access to the following methods: - [`add_attribute`](add_attribute.md) - [`association`](association.md) - [`sequence`](sequence.md) - [`trait`](trait.md) - [`method_missing`](method_missing.md) - [`transient`](transient.md) - [`traits_for_enum`](traits_for_enum.md) - [`initialize_with`](build-and-create.md#initialize_with) - [`skip_create`](build-and-create.md) - [`to_create`](build-and-create.md#to_create) - [`before`](hooks.md#after-and-before-methods) - [`after`](hooks.md#after-and-before-methods) - [`callback`](hooks.md#callback) - `factory` You can use `factory` inside a `factory` block to define a new factory with an implied parent. factory_bot-6.5.6/docs/src/ref/find_definitions.md000066400000000000000000000012141507676517300222120ustar00rootroot00000000000000# FactoryBot.find_definitions The `FactoryBot.find_definitions` method loads in all factory\_bot definitions across the project. The load order is controlled by the `FactoryBot.definition_file_paths` attribute. The default load order is: 1. `factories.rb` 1. `factories/**/*.rb` 1. `test/factories.rb` 1. `test/factories/**/*.rb` 1. `spec/factories.rb` 1. `spec/factories/**/*.rb` ## Rails The `.find_definitions` method is called automatically by `factory_bot_rails` after initialize. The `.definition_file_paths` can be set during initialization (e.g. `config/initializers`), or via `Rails.application.config.factory_bot.definition_file_paths`. factory_bot-6.5.6/docs/src/ref/hooks.md000066400000000000000000000026671507676517300200370ustar00rootroot00000000000000# Hooks Within a `factory` definition block and the `FactoryBot.define` block, you have access to the `after`, `before`, and `callback` methods. This allow you to hook into parts of the [build strategies]. [build strategies]: build-strategies.html Within a `factory` definition block, these callbacks are scoped to just that factory. Within a `FactoryBot.define` block, they are global to all factories. ## `callback` The `callback` method allows you to hook into any factory\_bot callback by name. The pre-defined names, as seen in the [build strategies] reference, are `before_all`, `after_build`, `before_create`, `after_create`, `after_stub`, and `after_all`. This method takes a splat of names, and a block. It invokes the block any time one of the names is activated. The block can be anything that responds to `#to_proc`. This block takes two arguments: the instance of the factory, and the factory\_bot context. The context holds [transient](transient.html) attributes. The same callback name can be hooked into multiple times. Every block is run, in the order it was defined. Callbacks are inherited from their parents; the parents' callbacks are run first. ## `after` and `before` methods The `after` and `before` methods add some nice syntax to `callback`: ```ruby after(:create) do |user, context| user.post_first_article(context.article) end callback(:after_create) do |user, context| user.post_first_article(context.article) end ``` factory_bot-6.5.6/docs/src/ref/lint.md000066400000000000000000000012351507676517300176500ustar00rootroot00000000000000# FactoryBot.lint The `FactoryBot.lint` method tries each factory and raises `FactoryBot::InvalidFactoryError` on failure. It can take the following optional arguments: - A splat of factory names. This will restrict the linting to just the ones listed. The default is all. - `:strategy` - the [build strategy] to use. The default is `:create`. - `:traits` - whether to try building each trait, too. The default is `false`. - `:verbose` - whether to show a stack trace on error. The default is `false`. [build strategy]: build-strategies.html Suggested techniques for hooking `.lint` into your system is discussed in [the guide](../linting-factories/summary.html). factory_bot-6.5.6/docs/src/ref/method_missing.md000066400000000000000000000037231507676517300217170ustar00rootroot00000000000000# method_missing With a `factory` definition block, you can use `add_attribute`, `association`, `sequence`, and `trait` to define a factory. You can also level a default `method_missing` definition for potential shortcuts. Calling an unknown method (e.g. `name`, `admin`, `email`, `account`) connects an association, sequence, trait, or attribute to the factory: 1. If the method missing is passed a block, it always defines an attribute. This allows you to set the value for the attribute. 1. If the method missing is passed a hash as a argument with the key `:factory`, then it always defines an association. This allows you to override the factory used for the association. 1. If there is another factory of the same name, then it defines an association. 1. If there is a global sequence of the same name, then it defines an attribute with a value that pulls from the sequence. 1. If there is a trait of the same name for that factory, then it turns that trait on for all builds of this factory. Using `method_missing` can turn an explicit definition: ```ruby FactoryBot.define do sequence(:email) { |n| "person#{n}@example.com" } factory :account factory :organization factory :user, traits: [:admin] do add_attribute(:name) { "Lord Nikon" } add_attribute(:email) { generate(:email) } association :account association :org, factory: :organization trait :admin do add_attribute(:admin) { true } end end end ``` ... into a more implicit definition: ```ruby FactoryBot.define do sequence(:email) { |n| "person#{n}@example.com" } factory :account factory :organization factory :user do name { "Lord Nikon" } # no more `add_attribute` admin # no more :traits email # no more `add_attribute` account # no more `association` org factory: :organization # no more `association` trait :admin do admin { true } end end end ``` factory_bot-6.5.6/docs/src/ref/modify.md000066400000000000000000000011031507676517300201630ustar00rootroot00000000000000# FactoryBot.modify The `FactoryBot.modify` class method defines a block with an _overriding_ `factory` method available. That is the only method you can call within the block. The `factory` method within this block takes a mandatory factory name, and a block. All other arguments are ignored. The factory name must already be defined. The block is a normal [factory definition block](factory.html). Take note that [hooks](hooks.html) cannot be cleared and continue to compound. For details on why you'd want to use this, see [the guide](../modifying-factories/summary.html). factory_bot-6.5.6/docs/src/ref/register_strategy.md000066400000000000000000000013751507676517300224550ustar00rootroot00000000000000# FactoryBot.register_strategy The `FactoryBot.register_strategy` method is how to add a [build strategy](build-strategies.html). It takes two mandatory arguments: name and class. The name is a Symbol, and registering it exposes a method under `FactoryBot::Syntax::Methods`. The class must define the methods `association` and `result`. The `association` method takes an instance of `FactoryRunner`. You can `#run` this runner, passing a strategy name (it defaults to the current one) and an optional block. The block is called after the association is built, and is passed the object that was built. The `result` method takes the object that was built for this factory (using `initalize_with`), and returns the result of this factory for this build strategy. factory_bot-6.5.6/docs/src/ref/sequence.md000066400000000000000000000027411507676517300205150ustar00rootroot00000000000000# sequence A factory\_bot set up supports two levels of sequences: global and factory-specific. ## Global sequences With a [`Factory.define`] block, use the `sequence` method to define global sequences that can be shared with other factories. [`Factory.define`]: define.html The `sequence` method takes a name, optional arguments, and a block. The name is expected to be a Symbol. The supported arguments are a number representing the starting value (default: `1`), and `:aliases` (default `[]`). The starting value must respond to `#next`. The block takes a value as an argument, and returns a result. The sequence value is incremented globally. Using an `:email_address` sequence from multiple places increments the value each time. See [method_missing](method_missing.html) for a shorthand. ## Factory sequences Sequences can be localized within `factory` blocks. The syntax is the same as for a global sequence, but the scope of the incremented value is limited to the factory definition. In addition, using `sequence` with a `factory` block implicitly calls `add_attribute` for that value. These two are similar, except the second example does not cause any global sequences to exist: ```ruby # A global sequence sequence(:user_factory_email) { |n| "person#{n}@example.com" } factory :user do # Using a global sequence email { generate(:user_factory_email) } end ``` ```ruby # A factory-scoped sequence factory :user do sequence(:email) { |n| "person#{n}@example.com" } end ``` factory_bot-6.5.6/docs/src/ref/trait.md000066400000000000000000000004751507676517300200320ustar00rootroot00000000000000# trait Within a `factory` definition block, use the `trait` method to define named permutations of the factory. The trait method takes a name (Symbol) and a block. Treat the block like you would a [`factory`] definition block. [`factory`]: factory.html See [method_missing](method_missing.html) for a shorthand. factory_bot-6.5.6/docs/src/ref/traits_for_enum.md000066400000000000000000000033301507676517300221000ustar00rootroot00000000000000# traits_for_enum With a `factory` definition block, the `traits_for_enum` method is a helper for any object with an attribute that can be one of a few values. The original inspiration was [`ActiveRecord::Enum`] but it can apply to any attribute with a restricted set of values. [`ActiveRecord::Enum`]: https://api.rubyonrails.org/classes/ActiveRecord/Enum.html This method creates a trait for each value. The `traits_for_enum` method takes a required attribute name and an optional set of values. The values can be any Enumerable, such as Array or Hash. By default, the values are `nil`. If the values are an Array, this method defines a trait for each element in the array. The trait's name is the array element, and it sets the attribute to the same array element. If the values are a Hash, this method defines traits based on the keys, setting the attribute to the values. The trait's name is the key, and it sets the attribute to the value. If the value is any other Enumerable, it treats it like an Array or Hash based on whether `#each` iterates in pairs like it does for Hashes. If the value is nil, it uses a class method named after the pluralized attribute name. ```ruby FactoryBot.define do factory :article do traits_for_enum :visibility, [:public, :private] # trait :public do # visibility { :public } # end # trait :private do # visibility { :private } # end traits_for_enum :collaborative, draft: 0, shared: 1 # trait :draft do # collaborative { 0 } # end # trait :shared do # collaborative { 1 } # end traits_for_enum :status # Article.statuses.each do |key, value| # trait key do # status { value } # end # end end end ``` factory_bot-6.5.6/docs/src/ref/transient.md000066400000000000000000000007471507676517300207200ustar00rootroot00000000000000# transient Within a `factory` definition block, the goal is to construct an instance of the class. While factory\_bot does this, it keeps track of data in a context. To set data on this context, use a `transient` block. Treat a `transient` block like a `factory` definition block. However, none of the attributes, associations, traits, or sequences you set will impact the final object. This is most useful when paired with [hooks](hooks.html) or [to_create](build-and-create.html). factory_bot-6.5.6/docs/src/sequences/000077500000000000000000000000001507676517300175765ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/sequences/aliases.md000066400000000000000000000013261507676517300215430ustar00rootroot00000000000000# Aliases Sequences can also have aliases. The sequence aliases share the same counter: ```ruby factory :user do sequence(:email, 1000, aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" } end # will increase value counter for :email which is shared by :sender and :receiver generate(:sender) ``` Define aliases and use default value (1) for the counter ```ruby factory :user do sequence(:email, aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" } end ``` Setting the value: ```ruby factory :user do sequence(:email, 'a', aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" } end ``` The value needs to support the `#next` method. Here the next value will be 'a', then 'b', etc. factory_bot-6.5.6/docs/src/sequences/as-implicit-attributes.md000066400000000000000000000005311507676517300245160ustar00rootroot00000000000000# As implicit attributes Or as implicit attributes: ```ruby FactoryBot.define do sequence :email do |n| "person#{n}@example.com" end end factory :user do email # Same as `email { generate(:email) }` end ``` Note that defining sequences as implicit attributes will not work if you have a factory with the same name as the sequence. factory_bot-6.5.6/docs/src/sequences/factory-sequences.md000066400000000000000000000007431507676517300235640ustar00rootroot00000000000000# Factory sequences And it's also possible to define a sequence that is only used in a particular factory: ```ruby factory :user do sequence(:email) { |n| "person#{n}@example.com" } end ``` With Ruby 2.7's support for [numbered parameters][], inline definitions can be even more abbreviated: ```ruby factory :user do sequence(:email) { "person#{_1}@example.com" } end ``` [numbered parameters]: https://ruby-doc.org/core-2.7.1/Proc.html#class-Proc-label-Numbered+parameters factory_bot-6.5.6/docs/src/sequences/generating.md000066400000000000000000000037241507676517300222510ustar00rootroot00000000000000# Generating a Sequence Being able to directly generate a sequence, without having to build the object can really speed up testing. This can be achieved by passing the [sequence URI](sequence-uris.md) to `:generate` for a single value or `:generate_list` for an Array of sequential values. ```ruby FactoryBot.define do sequence(:char, 'a') {|c| "global_character_#{c}" } factory :user do sequence(:name, %w[Jane Joe Josh Jayde John].to_enum) trait :with_age do sequence(:age, 21) end end end ## # char generate(:char) # "global_character_a" generate_list(:char, 2) # ["global_character_b", "global_character_c"] generate(:char) # "global_character_d" ## # user name generate(:user, :name) # "Jane" generate_list(:user, :name, 3) # ['Joe', 'Josh', 'Jayde'] generate(:user, :name) # "John" ## # user age generate(:user, :with_age, :age) # 21 generate_list(:user, :with_age, :age, 5) # [22, 23, 24, 25, 26] generate(:user, :with_age, :age) # 27 ``` ## Scope On occasion a sequence block may refer to a scoped attribute. In this case, the scope must be provided, or else an exception will be raised: ```ruby FactoryBot.define do factory :user do sequence(:email) { |n| "#{name}-#{n}@example.com" } end end generate(:user, :email) # ArgumentError, Sequence user:email failed to return a value. Perhaps it needs a scope to operate? (scope: ) jester = build(:user, name: "Jester") jester.email # "Jester-1@example.com" generate(:user, :email, scope: jester) # "Jester-2@example.com" generate_list(:user, :email, 2, scope: jester) # ["Jester-3@example.com", "Jester-4@example.com"] ``` When testing, the scope can be any object that responds to the referenced attributes: ```ruby require 'ostruct' FactoryBot.define factory :user do sequence(:info) { |n| "#{name}-#{n}-#{age + n}" } end end test_scope = OpenStruct.new(name: "Jester", age: 23) generate_list('user/info', 3, scope: test_scope) # ["Jester-1-24", "Jester-2-25", "Jester-3-26"] ``` factory_bot-6.5.6/docs/src/sequences/global-sequences.md000066400000000000000000000007151507676517300233540ustar00rootroot00000000000000# Global sequences Unique values in a specific format (for example, e-mail addresses) can be generated using sequences. Sequences are defined by calling `sequence` in a definition block, and values in a sequence are generated by calling `generate`: ```ruby # Defines a new sequence FactoryBot.define do sequence :email do |n| "person#{n}@example.com" end end generate :email # => "person1@example.com" generate :email # => "person2@example.com" ``` factory_bot-6.5.6/docs/src/sequences/initial-value.md000066400000000000000000000007511507676517300226660ustar00rootroot00000000000000# Initial value You can override the initial value. Any value that responds to the `#next` method will work (e.g. 1, 2, 3, 'a', 'b', 'c') ```ruby factory :user do sequence(:email, 1000) { |n| "person#{n}@example.com" } end ``` The initial value can also be lazily set by passing a Proc as the value. This Proc will be called the first time the `sequence.next` is called. ```ruby factory :user do sequence(:email, proc { Person.count + 1 }) { |n| "person#{n}@example.com" } end ``` factory_bot-6.5.6/docs/src/sequences/rewinding.md000066400000000000000000000023111507676517300221030ustar00rootroot00000000000000# Rewinding Sequences can also be rewound to their starting value: ## All sequences Rewind all global and factory sequences with `FactoryBot.rewind_sequences`: ```ruby FactoryBot.define do sequence(:email) {|n| "person#{n}@example.com" } factory :user do sequence(:email) {|n| "user#{n}@example.com" } end end generate(:email) # "person1@example.com" generate(:email) # "person2@example.com" generate(:email) # "person3@example.com" generate(:user, :email) # "user1@example.com" generate(:user, :email) # "user2@example.com" generate(:user, :email) # "user3@example.com" FactoryBot.rewind_sequences generate(:email) # "person1@example.com" generate(:user, :email) # "user1@example.com" ``` ## Individual sequences An individual sequence can be rewound by passing the [sequence URI](sequence-uris.md) to `FactoryBot.rewind_sequence`: ```ruby FactoryBot.define do sequence(:email) {|n| "global_email_#{n}@example.com" } factory :user do sequence(:email) {|n| "user_email_#{n}@example.com" } end end FactoryBot.rewind_sequence(:email) generate(:email) #=> "global_email_1@example.com" factoryBot.rewind_sequence(:user, :email) generate(:user, :email) #=> "user_email_1@example.com" ``` factory_bot-6.5.6/docs/src/sequences/sequence-uris.md000066400000000000000000000061461507676517300227170ustar00rootroot00000000000000# Sequence URIs There are many reasons to manipulate a specific sequence: - generating a single value with: `generate` - generating multiple values with: `generate_list` - setting it to a new value with `FactoryBot.set_sequence` - rewinding it with: `rewind_sequence` To accomplish this we need to be able to reference the desired sequence. This is achieved with its unique URI. ## URI Composition Each URI is composed of up to three names: | position | name | required | | :------: | -------------- | -------------------------------------------------------------------- | | 1. | factory name: | **if** - the sequence is defined within a Factory or a Factory Trait | | 2. | trait name: | **if** - the sequence is defined within a Trait | | 3. | sequence name: | **always required** | The URI can be entered as individual symbols: ```ruby generate(:my_factory_name, :my_trait_name, :my_sequence_name) ``` **or** as individual strings: ```ruby generate('my_factory_name', 'my_trait_name', 'my_sequence_name') ``` **or** as a single resource string: ```ruby generate("my_factory_name/my_trait_name/my_sequence_name") ``` ## Full URI example This example details all the possible scenarios, with the comments showing the URI used to generate a value for each specific sequence: ```ruby FactoryBot.define do sequence(:sequence) {|n| "global_sequence_#{n}"} # generate(:sequence) trait :global_trait do sequence(:sequence) {|n| "global_trait_sequence_#{n}"} # generate(:global_trait, :sequence) end factory :user do sequence(:sequence) {|n| "user_sequence_#{n}"} # generate(:user, :sequence) trait :user_trait do sequence(:sequence) {|n| "user_trait_sequence_#{n}"} # generate(:user, :user_trait, :sequence) end factory :author do sequence(:sequence) {|n| "author_sequence_#{n}"} # generate(:author, :sequence) trait :author_trait do sequence(:sequence) {|n| "author_trait_sequence_#{n}"} # generate(:author, :author_trait, :sequence) end end end end ``` ## Multi URIs It is possible for a single sequence to have multiple URIs. If the factory or trait has aliases, the sequence will have an additional URI for each alias, or combination of aliases. In this example, the same sequence can referenced in four different ways: ```ruby factory :user, aliases: [:author] do trait :user_trait, aliases: [:author_trait] do sequence(:sequence) {|n| "author_trait_sequence_#{n}"} end end # generate(:user, :user_trait, :sequence) # generate(:user, :author_trait, :sequence) # generate(:author, :user_trait, :sequence) # generate(:author, :author_trait, :sequence) ```
## Important - No matter how deeply nested, the factory name component of the URI is always the factory where the sequence is defined, not any parent factories. - If a factory inherits a sequence, the URI must reference the factory where it was defined, not the one in which it is used.
factory_bot-6.5.6/docs/src/sequences/setting-the-value.md000066400000000000000000000031301507676517300234620ustar00rootroot00000000000000# Setting the value When testing or working in the console, being able to set the sequence to a specific value, is incredibly helpful. This can be achieved by passing the [sequence URI](sequence-uris.md) and the new value to `FactoryBot.set_sequence`: ## Global Sequences Global sequences are set with the sequence name and the new value: ```ruby FactoryBot.define do sequence(:char, 'a') {|c| "global_character_#{c}" } factory :user do sequence(:name, %w[Jane Joe Josh Jayde John].to_enum) trait :with_email do sequence(:email) {|n| "user_#{n}@example.com" } end end end ## # char generate(:char) # "global_character_a" FactoryBot.set_sequence(:char, 'z') generate(:char) # "global_character_z" ## # user name generate(:user, :name) # "Jane" FactoryBot.set_sequence(:user, :name, 'Jayde') generate(:user, :name) # "Jayde" ## # user email generate(:user, :with_email, :email) # "user_1@example.com" FactoryBot.set_sequence(:user, :with_email, :email, 1_234_567) generate(:user, :with_email, :email) # "user_1234567@example.com" ```
## Note - The new value must match the sequence collection: You cannot pass a String to an Integer based sequence! - An integer based sequence, such as a record ID, can accept any positive integer as the value. - A fixed collection sequence can accept any value within the collection. - An unlimited sequence, such as a character `sequence(:unlimited,'a')` will timeout if not found within the default maximum search time of three seconds. - The timeout can be configured with: `FactoryBot.sequence_setting_timeout = 1.5`
factory_bot-6.5.6/docs/src/sequences/summary.md000066400000000000000000000000141507676517300216100ustar00rootroot00000000000000# Sequences factory_bot-6.5.6/docs/src/sequences/uniqueness.md000066400000000000000000000006511507676517300223210ustar00rootroot00000000000000# Uniqueness When working with uniqueness constraints, be careful not to pass in override values that will conflict with the generated sequence values. In this example the email will be the same for both users. If email must be unique, this code will error: ```rb factory :user do sequence(:email) { |n| "person#{n}@example.com" } end FactoryBot.create(:user, email: "person1@example.com") FactoryBot.create(:user) ``` factory_bot-6.5.6/docs/src/sequences/with-dynamic-attributes.md000066400000000000000000000003411507676517300246770ustar00rootroot00000000000000# With dynamic attributes Sequences can be used in dynamic attributes: ```ruby FactoryBot.define do sequence :email do |n| "person#{n}@example.com" end end factory :invite do invitee { generate(:email) } end ``` factory_bot-6.5.6/docs/src/sequences/without-a-block.md000066400000000000000000000005401507676517300231300ustar00rootroot00000000000000# Without a block Without a block, the value will increment itself, starting at its initial value: ```ruby factory :post do sequence(:position) end ``` Note that the value for the sequence could be any Enumerable instance, as long as it responds to `#next`: ```ruby factory :task do sequence :priority, %i[low medium high urgent].cycle end ``` factory_bot-6.5.6/docs/src/setup/000077500000000000000000000000001507676517300167435ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/setup/summary.md000066400000000000000000000015541507676517300207670ustar00rootroot00000000000000# Setup Installation varies based on the framework you are using, if any, and optionally the test framework. Since installation varies based on code that we do not control, those docs are kept up-to-date in [our wiki]. We encourage you to edit the wiki as the frameworks change. Below we document the most common setup. However, **we go into more detail in [our wiki]**. [our wiki]: https://github.com/thoughtbot/factory_bot/wiki/Installation ## Update Your Gemfile If you're using Rails: ```ruby gem "factory_bot_rails" ``` If you're *not* using Rails: ```ruby gem "factory_bot" ``` For more, see [our wiki]. ## Configure your test suite ### RSpec ```ruby RSpec.configure do |config| config.include FactoryBot::Syntax::Methods end ``` ### Test::Unit ```ruby class Test::Unit::TestCase include FactoryBot::Syntax::Methods end ``` For more, see [our wiki]. factory_bot-6.5.6/docs/src/traits/000077500000000000000000000000001507676517300171115ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/traits/as-implicit-attributes.md000066400000000000000000000005661507676517300240410ustar00rootroot00000000000000# As implicit attributes Traits can be used as implicit attributes: ```ruby factory :week_long_published_story_with_title, parent: :story do published week_long_publishing title { "Publishing that was started at #{start_at}" } end ``` Note that defining traits as implicit attributes will not work if you have a factory or sequence with the same name as the trait. factory_bot-6.5.6/docs/src/traits/attribute-precedence.md000066400000000000000000000013111507676517300235250ustar00rootroot00000000000000# Attribute precedence Traits that define the same attributes won't raise AttributeDefinitionErrors; the trait that defines the attribute last gets precedence. ```ruby factory :user do name { "Friendly User" } login { name } trait :active do name { "John Doe" } status { :active } login { "#{name} (active)" } end trait :inactive do name { "Jane Doe" } status { :inactive } login { "#{name} (inactive)" } end trait :admin do admin { true } login { "admin-#{name}" } end factory :active_admin, traits: [:active, :admin] # login will be "admin-John Doe" factory :inactive_admin, traits: [:admin, :inactive] # login will be "Jane Doe (inactive)" end ``` factory_bot-6.5.6/docs/src/traits/defining-traits.md000066400000000000000000000015231507676517300225230ustar00rootroot00000000000000# Defining traits Traits allow you to group attributes together and then apply them to any factory. ```ruby factory :user, aliases: [:author] factory :story do title { "My awesome story" } author trait :published do published { true } end trait :unpublished do published { false } end trait :week_long_publishing do start_at { 1.week.ago } end_at { Time.now } end trait :month_long_publishing do start_at { 1.month.ago } end_at { Time.now } end factory :week_long_published_story, traits: [:published, :week_long_publishing] factory :month_long_published_story, traits: [:published, :month_long_publishing] factory :week_long_unpublished_story, traits: [:unpublished, :week_long_publishing] factory :month_long_unpublished_story, traits: [:unpublished, :month_long_publishing] end ``` factory_bot-6.5.6/docs/src/traits/enum.md000066400000000000000000000031641507676517300204030ustar00rootroot00000000000000# Enum traits Given an Active Record model with an enum attribute: ```rb class Task < ActiveRecord::Base enum status: {queued: 0, started: 1, finished: 2} end ``` factory\_bot will automatically define traits for each possible value of the enum: ```rb FactoryBot.define do factory :task end FactoryBot.build(:task, :queued) FactoryBot.build(:task, :started) FactoryBot.build(:task, :finished) ``` Writing the traits out manually would be cumbersome, and is not necessary: ```rb FactoryBot.define do factory :task do trait :queued do status { :queued } end trait :started do status { :started } end trait :finished do status { :finished } end end end ``` If automatically defining traits for enum attributes on every factory is not desired, it is possible to disable the feature by setting `FactoryBot.automatically_define_enum_traits = false` In that case, it is still possible to explicitly define traits for an enum attribute in a particular factory: ```rb FactoryBot.automatically_define_enum_traits = false FactoryBot.define do factory :task do traits_for_enum(:status) end end ``` It is also possible to use this feature for other enumerable values, not specifically tied to Active Record enum attributes. With an array: ```rb class Task attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum(:status, ["queued", "started", "finished"]) end end ``` Or with a hash: ```rb class Task attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum(:status, { queued: 0, started: 1, finished: 2 }) end end ``` factory_bot-6.5.6/docs/src/traits/in-child-factories.md000066400000000000000000000005261507676517300231020ustar00rootroot00000000000000# In child factories You can override individual attributes granted by a trait in a child factory: ```ruby factory :user do name { "Friendly User" } login { name } trait :active do name { "John Doe" } status { :active } login { "#{name} (M)" } end factory :brandon do active name { "Brandon" } end end ``` factory_bot-6.5.6/docs/src/traits/mixins.md000066400000000000000000000005761507676517300207520ustar00rootroot00000000000000# As mixins Traits can be defined outside of factories and used as mixins to compose shared attributes: ```ruby FactoryBot.define do trait :timestamps do created_at { 8.days.ago } updated_at { 4.days.ago } end factory :user, traits: [:timestamps] do username { "john_doe" } end factory :post do timestamps title { "Traits rock" } end end ``` factory_bot-6.5.6/docs/src/traits/summary.md000066400000000000000000000015121507676517300211270ustar00rootroot00000000000000# Traits Traits allow you to group attributes together and then apply them to any factory. ```ruby factory :user, aliases: [:author] factory :story do title { "My awesome story" } author trait :published do published { true } end trait :unpublished do published { false } end trait :week_long_publishing do start_at { 1.week.ago } end_at { Time.now } end trait :month_long_publishing do start_at { 1.month.ago } end_at { Time.now } end factory :week_long_published_story, traits: [:published, :week_long_publishing] factory :month_long_published_story, traits: [:published, :month_long_publishing] factory :week_long_unpublished_story, traits: [:unpublished, :week_long_publishing] factory :month_long_unpublished_story, traits: [:unpublished, :month_long_publishing] end ``` factory_bot-6.5.6/docs/src/traits/traits-within-traits.md000066400000000000000000000004021507676517300235410ustar00rootroot00000000000000# Traits within traits Traits can be used within other traits to mix in their attributes. ```ruby factory :order do trait :completed do completed_at { 3.days.ago } end trait :refunded do completed refunded_at { 1.day.ago } end end ``` factory_bot-6.5.6/docs/src/traits/using.md000066400000000000000000000017671507676517300205730ustar00rootroot00000000000000# Using traits Traits can also be passed in as a list of Symbols when you construct an instance from factory\_bot. ```ruby factory :user do name { "Friendly User" } trait :active do name { "John Doe" } status { :active } end trait :admin do admin { true } end end # creates an admin user with :active status and name "Jon Snow" create(:user, :admin, :active, name: "Jon Snow") ``` This ability works with `build`, `build_stubbed`, `attributes_for`, and `create`. `create_list` and `build_list` methods are supported as well. Remember to pass the number of instances to create/build as second parameter, as documented in the "Building or Creating Multiple Records" section of this file. ```ruby factory :user do name { "Friendly User" } trait :active do name { "John Doe" } status { :active } end trait :admin do admin { true } end end # creates 3 admin users with :active status and name "Jon Snow" create_list(:user, 3, :admin, :active, name: "Jon Snow") ``` factory_bot-6.5.6/docs/src/traits/with-associations.md000066400000000000000000000013111507676517300230770ustar00rootroot00000000000000# With associations Traits can be used with associations easily too: ```ruby factory :user do name { "Friendly User" } trait :admin do admin { true } end end factory :post do association :user, :admin, name: 'John Doe' end # creates an admin user with name "John Doe" create(:post).user ``` When you're using association names that are different than the factory: ```ruby factory :user do name { "Friendly User" } trait :admin do admin { true } end end factory :post do association :author, :admin, factory: :user, name: 'John Doe' # or association :author, factory: [:user, :admin], name: 'John Doe' end # creates an admin user with name "John Doe" create(:post).author ``` factory_bot-6.5.6/docs/src/traits/with-transient-attributes.md000066400000000000000000000005221507676517300245760ustar00rootroot00000000000000# With transient attributes Traits can accept transient attributes. ```ruby factory :invoice do trait :with_amount do transient do amount { 1 } end after(:create) do |invoice, context| create :line_item, invoice: invoice, amount: context.amount end end end create :invoice, :with_amount, amount: 2 ``` factory_bot-6.5.6/docs/src/transient-attributes/000077500000000000000000000000001507676517300217765ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/transient-attributes/summary.md000066400000000000000000000006311507676517300240150ustar00rootroot00000000000000# Transient Attributes Transient attributes are attributes only available within the factory definition, and not set on the object being built. This allows for more complex logic inside factories. These are defined within a `transient` block: ```ruby factory :user do name { "Zero Cool" } birth_date { age&.years.ago } transient do age { 11 } # only used to set `birth_date` above end end ``` factory_bot-6.5.6/docs/src/transient-attributes/with-associations.md000066400000000000000000000006201507676517300257660ustar00rootroot00000000000000# With associations Transient [associations](../associations/summary.md) are not supported in factory\_bot. Associations within the transient block will be treated as regular, non-transient associations. If needed, you can generally work around this by building a factory within a transient attribute: ```ruby factory :post factory :user do transient do post { build(:post) } end end ``` factory_bot-6.5.6/docs/src/transient-attributes/with-attributes_for.md000066400000000000000000000002571507676517300263310ustar00rootroot00000000000000# With attributes_for Transient attributes will be ignored within `attributes_for` and won't be set on the model, even if the attribute exists or you attempt to override it. factory_bot-6.5.6/docs/src/transient-attributes/with-callbacks.md000066400000000000000000000010151507676517300252050ustar00rootroot00000000000000# With callbacks If you need to access the evaluated definition itself in a factory\_bot callback, you'll need to declare a second block argument (for the definition) and access transient attributes from there. This represents the final, evaluated value. ```ruby factory :user do transient do upcased { false } end name { "John Doe" } after(:create) do |user, context| user.name.upcase! if context.upcased end end create(:user).name #=> "John Doe" create(:user, upcased: true).name #=> "JOHN DOE" ``` factory_bot-6.5.6/docs/src/transient-attributes/with-other-attributes.md000066400000000000000000000005711507676517300266010ustar00rootroot00000000000000# With other attributes You can access transient attributes within other attributes (see [Dependent Attributes](../dependent-attributes/summary.md)): ```ruby factory :user do transient do rockstar { true } end name { "John Doe#{" - Rockstar" if rockstar}" } end create(:user).name #=> "John Doe - ROCKSTAR" create(:user, rockstar: false).name #=> "John Doe" ``` factory_bot-6.5.6/docs/src/using-factories/000077500000000000000000000000001507676517300207055ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/using-factories/attribute-overrides.md000066400000000000000000000012621507676517300252330ustar00rootroot00000000000000# Attribute overrides No matter which strategy is used, it's possible to override the defined attributes by passing a hash: ```ruby # Build a User instance and override the first_name property user = build(:user, first_name: "Joe") user.first_name # => "Joe" ``` Ruby 3.1's support for [omitting values][] from `Hash` literals dovetails with attribute overrides and provides an opportunity to limit the repetition of variable names: ```ruby first_name = "Joe" # Build a User instance and override the first_name property user = build(:user, first_name:) user.first_name # => "Joe" ``` [omitting values]: https://docs.ruby-lang.org/en/3.1/syntax/literals_rdoc.html#label-Hash+Literals factory_bot-6.5.6/docs/src/using-factories/build-strategies.md000066400000000000000000000016361507676517300245040ustar00rootroot00000000000000# Build strategies factory\_bot supports several different build strategies: `build`, `create`, `attributes_for` and `build_stubbed`: ```ruby # Returns a User instance that's not saved user = build(:user) # Returns a saved User instance user = create(:user) # Returns a hash of attributes, which can be used to build a User instance for example attrs = attributes_for(:user) # Integrates with Ruby 3.0's support for pattern matching assignment attributes_for(:user) => {email:, name:, **attrs} # Returns an object with all defined attributes stubbed out stub = build_stubbed(:user) # Passing a block to any of the methods above will yield the return object create(:user) do |user| user.posts.create(attributes_for(:post)) end ``` # build_stubbed and Marshal.dump Note that objects created with `build_stubbed` cannot be serialized with `Marshal.dump`, since factory\_bot defines singleton methods on these objects. factory_bot-6.5.6/docs/src/using-factories/build_stubbed-and-marshaldump.md000066400000000000000000000002721507676517300271120ustar00rootroot00000000000000# build_stubbed and Marshal.dump Note that objects created with `build_stubbed` cannot be serialized with `Marshal.dump`, since factory\_bot defines singleton methods on these objects. factory_bot-6.5.6/docs/src/using-factories/summary.md000066400000000000000000000000221507676517300227160ustar00rootroot00000000000000# Using factories factory_bot-6.5.6/docs/src/using-without-bundler/000077500000000000000000000000001507676517300220625ustar00rootroot00000000000000factory_bot-6.5.6/docs/src/using-without-bundler/summary.md000066400000000000000000000014551507676517300241060ustar00rootroot00000000000000# Using Without Bundler If you're not using Bundler, be sure to have the gem installed and call: ```ruby require 'factory_bot' ``` Once required, assuming you have a directory structure of `spec/factories` or `test/factories`, all you'll need to do is run: ```ruby FactoryBot.find_definitions ``` If you're using a separate directory structure for your factories, you can change the definition file paths before trying to find definitions: ```ruby FactoryBot.definition_file_paths = %w(custom_factories_directory) FactoryBot.find_definitions ``` If you don't have a separate directory of factories and would like to define them inline, that's possible as well: ```ruby require 'factory_bot' FactoryBot.define do factory :user do name { 'John Doe' } date_of_birth { 21.years.ago } end end ``` factory_bot-6.5.6/factory_bot.gemspec000066400000000000000000000030211507676517300177400ustar00rootroot00000000000000$LOAD_PATH << File.expand_path("lib", __dir__) require "factory_bot/version" Gem::Specification.new do |s| s.name = "factory_bot" s.version = FactoryBot::VERSION s.summary = "factory_bot provides a framework and DSL for defining and " \ "using model instance factories." s.description = "factory_bot provides a framework and DSL for defining and " \ "using factories - less error-prone, more explicit, and " \ "all-around easier to work with than fixtures." s.files = Dir.glob("lib/**/*") + %w[CONTRIBUTING.md GETTING_STARTED.md LICENSE NAME.md NEWS.md README.md .yardopts] s.require_path = "lib" s.required_ruby_version = Gem::Requirement.new(">= 3.0.0") s.authors = ["Josh Clayton", "Joe Ferris"] s.email = ["jclayton@thoughtbot.com", "jferris@thoughtbot.com"] s.homepage = "https://github.com/thoughtbot/factory_bot" s.metadata = { "changelog_uri" => "https://github.com/thoughtbot/factory_bot/blob/main/NEWS.md" } s.add_dependency("activesupport", ">= 6.1.0") s.add_development_dependency("activerecord") s.add_development_dependency("appraisal") s.add_development_dependency("aruba") s.add_development_dependency("mutex_m") s.add_development_dependency("ostruct") s.add_development_dependency("rake") s.add_development_dependency("rspec") s.add_development_dependency("rspec-its") s.add_development_dependency("standard") s.add_development_dependency("simplecov") s.add_development_dependency("yard") s.license = "MIT" end factory_bot-6.5.6/features/000077500000000000000000000000001507676517300157025ustar00rootroot00000000000000factory_bot-6.5.6/features/find_definitions.feature000066400000000000000000000043311507676517300225730ustar00rootroot00000000000000Feature: FactoryBot can find factory definitions correctly Scenario: Find definitions with a path Given a file named "awesome_factories.rb" with: """ FactoryBot.define do factory :awesome_category, :class => Category do name { "awesome!!!" } end end """ When "awesome_factories.rb" is added to FactoryBot's file definitions path And I create a "awesome_category" instance from FactoryBot Then I should find the following for the last category: | name | | awesome!!! | Scenario: Find definitions with an absolute path Given a file named "awesome_factories.rb" with: """ FactoryBot.define do factory :another_awesome_category, :class => Category do name { "awesome!!!" } end end """ When "awesome_factories.rb" is added to FactoryBot's file definitions path as an absolute path And I create a "another_awesome_category" instance from FactoryBot Then I should find the following for the last category: | name | | awesome!!! | Scenario: Find definitions with a folder Given a file named "nested/great_factories.rb" with: """ FactoryBot.define do factory :great_category, :class => Category do name { "great!!!" } end end """ When "nested" is added to FactoryBot's file definitions path And I create a "great_category" instance from FactoryBot Then I should find the following for the last category: | name | | great!!! | Scenario: Reload FactoryBot Given a file named "nested/reload_factories.rb" with: """ FactoryBot.define do sequence(:great) trait :admin do admin { true } end factory :handy_category, :class => Category do name { "handy" } end end """ When "nested" is added to FactoryBot's file definitions path And I append to "nested/reload_factories.rb" with: """ FactoryBot.modify do factory :handy_category do name { "HANDY!!!" } end end """ And I reload factories And I create a "handy_category" instance from FactoryBot Then I should find the following for the last category: | name | | HANDY!!! | factory_bot-6.5.6/features/step_definitions/000077500000000000000000000000001507676517300212505ustar00rootroot00000000000000factory_bot-6.5.6/features/step_definitions/database_steps.rb000066400000000000000000000003341507676517300245570ustar00rootroot00000000000000Then(/^I should find the following for the last category:$/) do |table| table.hashes.first.each do |key, value| expect(Category.last.attributes[key].to_s).to eq value end end Before do Category.delete_all end factory_bot-6.5.6/features/step_definitions/factory_bot_steps.rb000066400000000000000000000020111507676517300253200ustar00rootroot00000000000000module FactoryBotDefinitionsHelper def append_file_to_factory_bot_definitions_path(path_to_file) FactoryBot.definition_file_paths ||= [] FactoryBot.definition_file_paths << path_to_file end end World(FactoryBotDefinitionsHelper) When(/^"([^"]*)" is added to FactoryBot's file definitions path$/) do |file_name| new_factory_file = File.join(expand_path("."), file_name.gsub(".rb", "")) append_file_to_factory_bot_definitions_path(new_factory_file) step %(I find definitions) end When(/^"([^"]*)" is added to FactoryBot's file definitions path as an absolute path$/) do |file_name| new_factory_file = File.expand_path(File.join(expand_path("."), file_name.gsub(".rb", ""))) append_file_to_factory_bot_definitions_path(new_factory_file) step %(I find definitions) end When(/^I create a "([^"]*)" instance from FactoryBot$/) do |factory_name| FactoryBot.create(factory_name) end When(/^I find definitions$/) do FactoryBot.find_definitions end When(/^I reload factories$/) do FactoryBot.reload end factory_bot-6.5.6/features/support/000077500000000000000000000000001507676517300174165ustar00rootroot00000000000000factory_bot-6.5.6/features/support/env.rb000066400000000000000000000003541507676517300205350ustar00rootroot00000000000000PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), "..", "..")) require "simplecov" if RUBY_ENGINE == "ruby" $: << File.join(PROJECT_ROOT, "lib") require "active_record" require "factory_bot" require "aruba/cucumber" factory_bot-6.5.6/features/support/factories.rb000066400000000000000000000005311507676517300217210ustar00rootroot00000000000000ActiveRecord::Base.establish_connection( adapter: "sqlite3", database: ":memory:" ) class CreateSchema < ActiveRecord::Migration[5.0] def self.up create_table :categories, force: true do |t| t.string :name end end end CreateSchema.suppress_messages { CreateSchema.migrate(:up) } class Category < ActiveRecord::Base; end factory_bot-6.5.6/gemfiles/000077500000000000000000000000001507676517300156575ustar00rootroot00000000000000factory_bot-6.5.6/gemfiles/6.1.gemfile000066400000000000000000000004441507676517300175170ustar00rootroot00000000000000# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord-jdbcsqlite3-adapter", "~> 61.0", platforms: [:jruby] gem "sqlite3", "~> 1.4", platforms: [:ruby] gem "activerecord", "~> 6.1.0" gem "concurrent-ruby", "< 1.3.5" gemspec name: "factory_bot", path: "../" factory_bot-6.5.6/gemfiles/7.0.gemfile000066400000000000000000000004441507676517300175170ustar00rootroot00000000000000# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord-jdbcsqlite3-adapter", "~> 70.0", platforms: [:jruby] gem "sqlite3", "~> 1.4", platforms: [:ruby] gem "activerecord", "~> 7.0.0" gem "concurrent-ruby", "< 1.3.5" gemspec name: "factory_bot", path: "../" factory_bot-6.5.6/gemfiles/7.1.gemfile000066400000000000000000000003701507676517300175160ustar00rootroot00000000000000# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord-jdbcsqlite3-adapter", platforms: [:jruby] gem "sqlite3", "~> 1.4", platforms: [:ruby] gem "activerecord", "~> 7.1.0" gemspec name: "factory_bot", path: "../" factory_bot-6.5.6/gemfiles/7.2.gemfile000066400000000000000000000003561507676517300175230ustar00rootroot00000000000000# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord-jdbcsqlite3-adapter", platforms: [:jruby] gem "sqlite3", platforms: [:ruby] gem "activerecord", "~> 7.2.0" gemspec name: "factory_bot", path: "../" factory_bot-6.5.6/gemfiles/main.gemfile000066400000000000000000000004501507676517300201340ustar00rootroot00000000000000# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord-jdbcsqlite3-adapter", "~> 70.0", platforms: [:jruby] gem "sqlite3", platforms: [:ruby] gem "activerecord", git: "https://github.com/rails/rails.git", branch: "main" gemspec name: "factory_bot", path: "../" factory_bot-6.5.6/lib/000077500000000000000000000000001507676517300146325ustar00rootroot00000000000000factory_bot-6.5.6/lib/factory_bot.rb000066400000000000000000000102241507676517300174710ustar00rootroot00000000000000require "set" require "active_support" require "active_support/core_ext/module/delegation" require "active_support/core_ext/module/attribute_accessors" require "active_support/deprecation" require "active_support/notifications" require "factory_bot/internal" require "factory_bot/definition_hierarchy" require "factory_bot/configuration" require "factory_bot/errors" require "factory_bot/factory_runner" require "factory_bot/strategy_syntax_method_registrar" require "factory_bot/strategy" require "factory_bot/registry" require "factory_bot/null_factory" require "factory_bot/null_object" require "factory_bot/evaluation" require "factory_bot/factory" require "factory_bot/attribute_assigner" require "factory_bot/evaluator" require "factory_bot/evaluator_class_definer" require "factory_bot/attribute" require "factory_bot/callback" require "factory_bot/callbacks_observer" require "factory_bot/declaration_list" require "factory_bot/declaration" require "factory_bot/sequence" require "factory_bot/attribute_list" require "factory_bot/trait" require "factory_bot/enum" require "factory_bot/aliases" require "factory_bot/definition" require "factory_bot/definition_proxy" require "factory_bot/syntax" require "factory_bot/syntax_runner" require "factory_bot/find_definitions" require "factory_bot/reload" require "factory_bot/decorator" require "factory_bot/decorator/attribute_hash" require "factory_bot/decorator/disallows_duplicates_registry" require "factory_bot/decorator/invocation_tracker" require "factory_bot/decorator/new_constructor" require "factory_bot/uri_manager" require "factory_bot/linter" require "factory_bot/version" module FactoryBot Deprecation = ActiveSupport::Deprecation.new("7.0", "factory_bot") mattr_accessor :use_parent_strategy, instance_accessor: false self.use_parent_strategy = true mattr_accessor :automatically_define_enum_traits, instance_accessor: false self.automatically_define_enum_traits = true mattr_accessor :sequence_setting_timeout, instance_accessor: false self.sequence_setting_timeout = 3 # Look for errors in factories and (optionally) their traits. # Parameters: # factories - which factories to lint; omit for all factories # options: # traits: true - to lint traits as well as factories # strategy: :create - to specify the strategy for linting # verbose: true - to include full backtraces for each linting error def self.lint(*args) options = args.extract_options! factories_to_lint = args[0] || FactoryBot.factories Linter.new(factories_to_lint, **options).lint! end # Set the starting value for ids when using the build_stubbed strategy # # @param [Integer] starting_id The new starting id value. def self.build_stubbed_starting_id=(starting_id) Strategy::Stub.next_id = starting_id - 1 end class << self # @!method rewind_sequence(*uri_parts) # Rewind an individual global or inline sequence. # # @param [Array, String] uri_parts The components of the sequence URI. # # @example Rewinding a sequence by its URI parts # rewind_sequence(:factory_name, :trait_name, :sequence_name) # # @example Rewinding a sequence by its URI string # rewind_sequence("factory_name/trait_name/sequence_name") # # @!method set_sequence(*uri_parts, value) # Set the sequence to a specific value, providing the new value is within # the sequence set. # # @param [Array, String] uri_parts The components of the sequence URI. # @param [Object] value The new value for the sequence. This must be a value that is # within the sequence definition. For example, you cannot set # a String sequence to an Integer value. # # @example # set_sequence(:factory_name, :trait_name, :sequence_name, 450) # @example # set_sequence([:factory_name, :trait_name, :sequence_name], 450) # @example # set_sequence("factory_name/trait_name/sequence_name", 450) delegate :factories, :register_strategy, :rewind_sequences, :rewind_sequence, :set_sequence, :strategy_by_name, to: Internal end end FactoryBot::Internal.register_default_strategies factory_bot-6.5.6/lib/factory_bot/000077500000000000000000000000001507676517300171455ustar00rootroot00000000000000factory_bot-6.5.6/lib/factory_bot/aliases.rb000066400000000000000000000005301507676517300211110ustar00rootroot00000000000000module FactoryBot class << self attr_accessor :aliases end self.aliases = [ [/(.+)_id/, '\1'], [/(.*)/, '\1_id'] ] def self.aliases_for(attribute) aliases.map { |(pattern, replace)| if pattern.match?(attribute) attribute.to_s.sub(pattern, replace).to_sym end }.compact << attribute end end factory_bot-6.5.6/lib/factory_bot/attribute.rb000066400000000000000000000007361507676517300215030ustar00rootroot00000000000000require "factory_bot/attribute/dynamic" require "factory_bot/attribute/association" require "factory_bot/attribute/sequence" module FactoryBot # @api private class Attribute attr_reader :name, :ignored def initialize(name, ignored) @name = name.to_sym @ignored = ignored end def to_proc -> {} end def association? false end def alias_for?(attr) FactoryBot.aliases_for(attr).include?(name) end end end factory_bot-6.5.6/lib/factory_bot/attribute/000077500000000000000000000000001507676517300211505ustar00rootroot00000000000000factory_bot-6.5.6/lib/factory_bot/attribute/association.rb000066400000000000000000000011121507676517300240040ustar00rootroot00000000000000module FactoryBot class Attribute # @api private class Association < Attribute attr_reader :factory def initialize(name, factory, overrides) super(name, false) @factory = factory @overrides = overrides end def to_proc factory = @factory overrides = @overrides traits_and_overrides = [factory, overrides].flatten factory_name = traits_and_overrides.shift -> { association(factory_name, *traits_and_overrides) } end def association? true end end end end factory_bot-6.5.6/lib/factory_bot/attribute/dynamic.rb000066400000000000000000000010021507676517300231120ustar00rootroot00000000000000module FactoryBot class Attribute # @api private class Dynamic < Attribute def initialize(name, ignored, block) super(name, ignored) @block = block end def to_proc block = @block -> { value = case block.arity when 1, -1, -2 then instance_exec(self, &block) else instance_exec(&block) end raise SequenceAbuseError if FactoryBot::Sequence === value value } end end end end factory_bot-6.5.6/lib/factory_bot/attribute/sequence.rb000066400000000000000000000005011507676517300233010ustar00rootroot00000000000000module FactoryBot class Attribute # @api private class Sequence < Attribute def initialize(name, sequence, ignored) super(name, ignored) @sequence = sequence end def to_proc sequence = @sequence -> { FactoryBot.generate(sequence) } end end end end factory_bot-6.5.6/lib/factory_bot/attribute_assigner.rb000066400000000000000000000136011507676517300233710ustar00rootroot00000000000000module FactoryBot # @api private class AttributeAssigner def initialize(evaluator, build_class, &instance_builder) @build_class = build_class @instance_builder = instance_builder @evaluator = evaluator @attribute_list = evaluator.class.attribute_list @attribute_names_assigned = [] end # constructs an object-based factory product def object @evaluator.instance = build_class_instance build_class_instance.tap do |instance| attributes_to_set_on_instance.each do |attribute| instance.public_send(:"#{attribute}=", get(attribute)) @attribute_names_assigned << attribute end end end # constructs a Hash-based factory product def hash @evaluator.instance = build_hash attributes_to_set_on_hash.each_with_object({}) do |attribute, result| result[attribute] = get(attribute) end end private # Track evaluation of methods on the evaluator to prevent the duplicate # assignment of attributes accessed and via `initialize_with` syntax def method_tracking_evaluator @method_tracking_evaluator ||= Decorator::AttributeHash.new( decorated_evaluator, attribute_names_to_assign ) end def decorated_evaluator Decorator::NewConstructor.new( Decorator::InvocationTracker.new(@evaluator), @build_class ) end def methods_invoked_on_evaluator method_tracking_evaluator.__invoked_methods__ end def build_class_instance @build_class_instance ||= method_tracking_evaluator.instance_exec(&@instance_builder) end def build_hash @build_hash ||= NullObject.new(hash_instance_methods_to_respond_to) end def get(attribute_name) @evaluator.send(attribute_name) end def attributes_to_set_on_instance (attribute_names_to_assign - @attribute_names_assigned - methods_invoked_on_evaluator).uniq end def attributes_to_set_on_hash attribute_names_to_assign - association_names end # Builds a list of attributes names that should be assigned to the factory product def attribute_names_to_assign @attribute_names_to_assign ||= begin # start a list of candidates containing non-transient attributes and overrides assignment_candidates = non_ignored_attribute_names + override_names # then remove any transient attributes (potentially reintroduced by the overrides), # and remove ignorable aliased attributes from the candidate list assignment_candidates - ignored_attribute_names - attribute_names_overriden_by_alias end end def non_ignored_attribute_names @attribute_list.non_ignored.names end def ignored_attribute_names @attribute_list.ignored.names end def association_names @attribute_list.associations.names end def override_names @evaluator.__override_names__ end def attribute_names @attribute_list.names end def hash_instance_methods_to_respond_to attribute_names + override_names + @build_class.instance_methods end # Builds a list of attribute names which are slated to be interrupted by an override. def attribute_names_overriden_by_alias @attribute_list .non_ignored .flat_map { |attribute| override_names.map do |override| attribute.name if ignorable_alias?(attribute, override) end } .compact end # Is the attribute an ignorable alias of the override? # An attribute is ignorable when it is an alias of the override AND it is # either interrupting an assocciation OR is not the name of another attribute # # @note An "alias" is currently an overloaded term for two distinct cases: # (1) attributes which are aliases and reference the same value # (2) a logical grouping of a foreign key and an associated object def ignorable_alias?(attribute, override) return false unless attribute.alias_for?(override) # The attribute alias should be ignored when the override interrupts an association return true if override_interrupts_association?(attribute, override) # Remaining aliases should be ignored when the override does not match a declared attribute. # An override which is an alias to a declared attribute should not interrupt the aliased # attribute and interrupt only the attribute with a matching name. This workaround allows a # factory to declare both and _id as separate and distinct attributes. !override_matches_declared_attribute?(override) end # Does this override interrupt an association? # When true, this indicates the aliased attribute is related to a declared association and the # override does not match the attribute name. # # @note Association overrides should take precedence over a declared foreign key attribute. # # @note An override may interrupt an association by providing the associated object or # by providing the foreign key. # # @param [FactoryBot::Attribute] aliased_attribute # @param [Symbol] override name of an override which is an alias to the attribute name def override_interrupts_association?(aliased_attribute, override) (aliased_attribute.association? || association_names.include?(override)) && aliased_attribute.name != override end # Does this override match the name of any declared attribute? # # @note Checking against the names of all attributes, resolves any issues with having both # and _id in the same factory. This also takes into account ignored # attributes that should not be assigned (aka transient attributes) # # @param [Symbol] override the name of an override def override_matches_declared_attribute?(override) attribute_names.include?(override) end end end factory_bot-6.5.6/lib/factory_bot/attribute_list.rb000066400000000000000000000030511507676517300225270ustar00rootroot00000000000000module FactoryBot # @api private class AttributeList include Enumerable def initialize(name = nil, attributes = []) @name = name @attributes = attributes end def define_attribute(attribute) ensure_attribute_not_self_referencing! attribute ensure_attribute_not_defined! attribute add_attribute attribute end def each(&block) @attributes.each(&block) end def names map(&:name) end def associations AttributeList.new(@name, select(&:association?)) end def ignored AttributeList.new(@name, select(&:ignored)) end def non_ignored AttributeList.new(@name, reject(&:ignored)) end def apply_attributes(attributes_to_apply) attributes_to_apply.each { |attribute| add_attribute(attribute) } end private def add_attribute(attribute) @attributes << attribute attribute end def ensure_attribute_not_defined!(attribute) if attribute_defined?(attribute.name) raise AttributeDefinitionError, "Attribute already defined: #{attribute.name}" end end def ensure_attribute_not_self_referencing!(attribute) if attribute.respond_to?(:factory) && attribute.factory == @name message = "Self-referencing association '#{attribute.name}' in '#{attribute.factory}'" raise AssociationDefinitionError, message end end def attribute_defined?(attribute_name) @attributes.any? do |attribute| attribute.name == attribute_name end end end end factory_bot-6.5.6/lib/factory_bot/callback.rb000066400000000000000000000011711507676517300212260ustar00rootroot00000000000000module FactoryBot class Callback attr_reader :name def initialize(name, block) @name = name.to_sym @block = block end def run(instance, evaluator) case block.arity when 1, -1, -2 then syntax_runner.instance_exec(instance, &block) when 2 then syntax_runner.instance_exec(instance, evaluator, &block) else syntax_runner.instance_exec(&block) end end def ==(other) name == other.name && block == other.block end protected attr_reader :block private def syntax_runner @syntax_runner ||= SyntaxRunner.new end end end factory_bot-6.5.6/lib/factory_bot/callbacks_observer.rb000066400000000000000000000017001507676517300233160ustar00rootroot00000000000000module FactoryBot # @api private class CallbacksObserver def initialize(callbacks, evaluator) @callbacks = callbacks @evaluator = evaluator @completed = [] end def update(name, result_instance) callbacks_by_name(name).each do |callback| if !completed?(result_instance, callback) callback.run(result_instance, @evaluator) record_completion!(result_instance, callback) end end end private def callbacks_by_name(name) @callbacks.select { |callback| callback.name == name } end def completed?(instance, callback) key = completion_key_for(instance, callback) @completed.include?(key) end def record_completion!(instance, callback) key = completion_key_for(instance, callback) @completed << key end def completion_key_for(instance, callback) "#{instance.object_id}-#{callback.object_id}" end end end factory_bot-6.5.6/lib/factory_bot/configuration.rb000066400000000000000000000016141507676517300223430ustar00rootroot00000000000000module FactoryBot # @api private class Configuration attr_reader( :callback_names, :factories, :inline_sequences, :sequences, :strategies, :traits ) def initialize @factories = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Factory")) @sequences = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Sequence")) @traits = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Trait")) @strategies = Registry.new("Strategy") @callback_names = Set.new @definition = Definition.new(:configuration) @inline_sequences = [] to_create(&:save!) initialize_with { new } end delegate :to_create, :skip_create, :constructor, :before, :after, :callback, :callbacks, to: :@definition def initialize_with(&block) @definition.define_constructor(&block) end end end factory_bot-6.5.6/lib/factory_bot/declaration.rb000066400000000000000000000006161507676517300217620ustar00rootroot00000000000000require "factory_bot/declaration/dynamic" require "factory_bot/declaration/association" require "factory_bot/declaration/implicit" module FactoryBot # @api private class Declaration attr_reader :name def initialize(name, ignored = false) @name = name @ignored = ignored end def to_attributes build end protected attr_reader :ignored end end factory_bot-6.5.6/lib/factory_bot/declaration/000077500000000000000000000000001507676517300214325ustar00rootroot00000000000000factory_bot-6.5.6/lib/factory_bot/declaration/association.rb000066400000000000000000000026251507676517300243000ustar00rootroot00000000000000module FactoryBot class Declaration # @api private class Association < Declaration def initialize(name, *options) super(name, false) @options = options.dup @overrides = options.extract_options! @factory_name = @overrides.delete(:factory) || name @traits = options end def ==(other) self.class == other.class && name == other.name && options == other.options end protected attr_reader :options private attr_reader :factory_name, :overrides, :traits def build raise_if_arguments_are_declarations! [ Attribute::Association.new( name, factory_name, [traits, overrides].flatten ) ] end def raise_if_arguments_are_declarations! if factory_name.is_a?(Declaration) raise ArgumentError.new(<<~MSG) Association '#{name}' received an invalid factory argument. Did you mean? 'factory: :#{factory_name.name}' MSG end overrides.each do |attribute, value| if value.is_a?(Declaration) raise ArgumentError.new(<<~MSG) Association '#{name}' received an invalid attribute override. Did you mean? '#{attribute}: :#{value.name}' MSG end end end end end end factory_bot-6.5.6/lib/factory_bot/declaration/dynamic.rb000066400000000000000000000010311507676517300233760ustar00rootroot00000000000000module FactoryBot class Declaration # @api private class Dynamic < Declaration def initialize(name, ignored = false, block = nil) super(name, ignored) @block = block end def ==(other) self.class == other.class && name == other.name && ignored == other.ignored && block == other.block end protected attr_reader :block private def build [Attribute::Dynamic.new(name, @ignored, @block)] end end end end factory_bot-6.5.6/lib/factory_bot/declaration/implicit.rb000066400000000000000000000016631507676517300235770ustar00rootroot00000000000000module FactoryBot class Declaration # @api private class Implicit < Declaration def initialize(name, factory = nil, ignored = false) super(name, ignored) @factory = factory end def ==(other) self.class == other.class && name == other.name && factory == other.factory && ignored == other.ignored end protected attr_reader :factory private def build if FactoryBot.factories.registered?(name) [Attribute::Association.new(name, name, {})] elsif FactoryBot::Internal.sequences.registered?(name) [Attribute::Sequence.new(name, name, @ignored)] elsif @factory.name.to_s == name.to_s message = "Self-referencing trait '#{@name}'" raise TraitDefinitionError, message else @factory.inherit_traits([name]) [] end end end end end factory_bot-6.5.6/lib/factory_bot/declaration_list.rb000066400000000000000000000017221507676517300230140ustar00rootroot00000000000000module FactoryBot # @api private class DeclarationList include Enumerable def initialize(name = nil) @declarations = [] @name = name @overridable = false end def declare_attribute(declaration) delete_declaration(declaration) if overridable? @declarations << declaration declaration end def overridable @overridable = true end def attributes @attributes ||= AttributeList.new(@name).tap do |list| to_attributes.each do |attribute| list.define_attribute(attribute) end end end def each(&block) @declarations.each(&block) end private def delete_declaration(declaration) @declarations.delete_if { |decl| decl.name == declaration.name } end def to_attributes @declarations.reduce([]) { |result, declaration| result + declaration.to_attributes } end def overridable? @overridable end end end factory_bot-6.5.6/lib/factory_bot/decorator.rb000066400000000000000000000007561507676517300214640ustar00rootroot00000000000000module FactoryBot class Decorator < BasicObject undef_method :== def initialize(component) @component = component end def method_missing(...) # rubocop:disable Style/MethodMissingSuper @component.send(...) end def send(...) __send__(...) end def respond_to_missing?(name, include_private = false) @component.respond_to?(name, true) || super end def self.const_missing(name) ::Object.const_get(name) end end end factory_bot-6.5.6/lib/factory_bot/decorator/000077500000000000000000000000001507676517300211275ustar00rootroot00000000000000factory_bot-6.5.6/lib/factory_bot/decorator/attribute_hash.rb000066400000000000000000000006031507676517300244610ustar00rootroot00000000000000module FactoryBot class Decorator class AttributeHash < Decorator def initialize(component, attributes = []) super(component) @attributes = attributes end def attributes @attributes.each_with_object({}) do |attribute_name, result| result[attribute_name] = @component.send(attribute_name) end end end end end factory_bot-6.5.6/lib/factory_bot/decorator/disallows_duplicates_registry.rb000066400000000000000000000005141507676517300276220ustar00rootroot00000000000000module FactoryBot class Decorator class DisallowsDuplicatesRegistry < Decorator def register(name, item) if registered?(name) raise DuplicateDefinitionError, "#{@component.name} already registered: #{name}" else @component.register(name, item) end end end end end factory_bot-6.5.6/lib/factory_bot/decorator/invocation_tracker.rb000066400000000000000000000007361507676517300253460ustar00rootroot00000000000000module FactoryBot class Decorator class InvocationTracker < Decorator def initialize(component) super @invoked_methods = [] end def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing @invoked_methods << name super end ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true) def __invoked_methods__ @invoked_methods.uniq end end end end factory_bot-6.5.6/lib/factory_bot/decorator/new_constructor.rb000066400000000000000000000003661507676517300247170ustar00rootroot00000000000000module FactoryBot class Decorator class NewConstructor < Decorator def initialize(component, build_class) super(component) @build_class = build_class end delegate :new, to: :@build_class end end end factory_bot-6.5.6/lib/factory_bot/definition.rb000066400000000000000000000124551507676517300216310ustar00rootroot00000000000000module FactoryBot # @api private class Definition attr_reader :defined_traits, :declarations, :name, :registered_enums, :uri_manager attr_accessor :klass def initialize(name, base_traits = [], **opts) @name = name @uri_manager = opts[:uri_manager] @declarations = DeclarationList.new(name) @callbacks = [] @defined_traits = Set.new @registered_enums = [] @to_create = nil @base_traits = base_traits @additional_traits = [] @constructor = nil @attributes = nil @compiled = false @expanded_enum_traits = false end delegate :declare_attribute, to: :declarations def attributes @attributes ||= AttributeList.new.tap do |attribute_list| attribute_lists = aggregate_from_traits_and_self(:attributes) { declarations.attributes } attribute_lists.each do |attributes| attribute_list.apply_attributes attributes end end end def to_create(&block) if block @to_create = block else aggregate_from_traits_and_self(:to_create) { @to_create }.last end end def constructor aggregate_from_traits_and_self(:constructor) { @constructor }.last end def callbacks aggregate_from_traits_and_self(:callbacks) { @callbacks } end def compile(klass = nil) unless @compiled expand_enum_traits(klass) unless klass.nil? declarations.attributes self.klass ||= klass defined_traits.each do |defined_trait| defined_trait.klass ||= klass base_traits.each { |bt| bt.define_trait defined_trait } additional_traits.each { |at| at.define_trait defined_trait } end @compiled = true ActiveSupport::Notifications.instrument "factory_bot.compile_factory", { name: name, attributes: declarations.attributes, traits: defined_traits, class: klass || self.klass } end end def overridable declarations.overridable self end def inherit_traits(new_traits) @base_traits += new_traits end def append_traits(new_traits) @additional_traits += new_traits end def add_callback(callback) @callbacks << callback end def skip_create @to_create = ->(instance) {} end def define_trait(trait) @defined_traits.add(trait) end def defined_traits_names @defined_traits.map(&:name) end def register_enum(enum) @registered_enums << enum end def define_constructor(&block) @constructor = block end def before(*names, &block) callback(*names.map { |name| "before_#{name}" }, &block) end def after(*names, &block) callback(*names.map { |name| "after_#{name}" }, &block) end def callback(*names, &block) names.each do |name| add_callback(Callback.new(name, block)) end end private def base_traits @base_traits.map { |name| trait_by_name(name) } rescue KeyError => error raise error_with_definition_name(error) end # detailed_message introduced in Ruby 3.2 for cleaner integration with # did_you_mean. See https://bugs.ruby-lang.org/issues/18564 if KeyError.method_defined?(:detailed_message) def error_with_definition_name(error) message = error.message + " referenced within \"#{name}\" definition" error.class.new(message, key: error.key, receiver: error.receiver) .tap { |new_error| new_error.set_backtrace(error.backtrace) } end else def error_with_definition_name(error) message = error.message message.insert( message.index("\nDid you mean?") || message.length, " referenced within \"#{name}\" definition" ) error.class.new(message).tap do |new_error| new_error.set_backtrace(error.backtrace) end end end def additional_traits @additional_traits.map { |name| trait_by_name(name) } end def trait_by_name(name) trait_for(name) || Internal.trait_by_name(name, klass) end def trait_for(name) @defined_traits_by_name ||= defined_traits.each_with_object({}) { |t, memo| memo[t.name] ||= t } @defined_traits_by_name[name.to_s] end def initialize_copy(source) super @attributes = nil @compiled = false @defined_traits_by_name = nil end def aggregate_from_traits_and_self(method_name, &block) compile [ base_traits.map(&method_name), instance_exec(&block), additional_traits.map(&method_name) ].flatten.compact end def expand_enum_traits(klass) return if @expanded_enum_traits if automatically_register_defined_enums?(klass) automatically_register_defined_enums(klass) end registered_enums.each do |enum| traits = enum.build_traits(klass) traits.each { |trait| define_trait(trait) } end @expanded_enum_traits = true end def automatically_register_defined_enums(klass) klass.defined_enums.each_key { |name| register_enum(Enum.new(name)) } end def automatically_register_defined_enums?(klass) FactoryBot.automatically_define_enum_traits && klass.respond_to?(:defined_enums) end end end factory_bot-6.5.6/lib/factory_bot/definition_hierarchy.rb000066400000000000000000000015501507676517300236610ustar00rootroot00000000000000module FactoryBot class DefinitionHierarchy delegate :callbacks, :constructor, :to_create, to: Internal def self.build_from_definition(definition) build_to_create(&definition.to_create) build_constructor(&definition.constructor) add_callbacks definition.callbacks end def self.add_callbacks(callbacks) if callbacks.any? define_method :callbacks do super() + callbacks end end end private_class_method :add_callbacks def self.build_constructor(&block) if block define_method(:constructor) do block end end end private_class_method :build_constructor def self.build_to_create(&block) if block define_method(:to_create) do block end end end private_class_method :build_to_create end end factory_bot-6.5.6/lib/factory_bot/definition_proxy.rb000066400000000000000000000166171507676517300230760ustar00rootroot00000000000000module FactoryBot class DefinitionProxy UNPROXIED_METHODS = %w[ __send__ __id__ nil? send object_id extend instance_eval initialize block_given? raise caller method ].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) proxy = DefinitionProxy.new(@definition, true) proxy.instance_eval(&block) end # Calls add_attribute using the missing method name as the name of the # attribute, so that: # # factory :user do # name { 'Billy Idol' } # end # # and: # # factory :user do # add_attribute(:name) { 'Billy Idol' } # end # # are equivalent. # # If no argument or block is given, factory_bot will first look for an # association, then for a sequence, and finally for a trait with the same # name. This means that given an "admin" trait, an "email" sequence, and an # "account" factory: # # factory :user, traits: [:admin] do # email { generate(:email) } # association :account # end # # and: # # factory :user do # admin # email # account # end # # are equivalent. def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing association_options = args.first if association_options.nil? __declare_attribute__(name, block) elsif __valid_association_options?(association_options) association(name, association_options) else raise NoMethodError.new(<<~MSG) undefined method '#{name}' in '#{@definition.name}' factory Did you mean? '#{name} { #{association_options.inspect} }' MSG end end # Adds an attribute that will have unique values generated by a sequence with # a specified format. # # The result of: # factory :user do # sequence(:email) { |n| "person#{n}@example.com" } # end # # Is equal to: # sequence(:email) { |n| "person#{n}@example.com" } # # factory :user do # email { FactoryBot.generate(:email) } # end # # Except that no globally available sequence will be defined. def sequence(name, *args, &block) options = args.extract_options! options[:uri_paths] = @definition.uri_manager.to_a args << options new_sequence = Sequence.new(name, *args, &block) registered_sequence = __fetch_or_register_sequence(new_sequence) add_attribute(name) { increment_sequence(registered_sequence) } end # Adds an attribute that builds an association. The associated instance will # be built using the same build strategy as the parent instance. # # Example: # factory :user do # name 'Joey' # end # # factory :post do # association :author, factory: :user # end # # Arguments: # * name: +Symbol+ # The name of this attribute. # * options: +Hash+ # # Options: # * factory: +Symbol+ or +String+ # The name of the factory to use when building the associated instance. # If no name is given, the name of the attribute is assumed to be the # name of the factory. For example, a "user" association will by # default use the "user" factory. def association(name, *options) if block_given? raise AssociationDefinitionError.new( "Unexpected block passed to '#{name}' association " \ "in '#{@definition.name}' factory" ) else declaration = Declaration::Association.new(name, *options) @definition.declare_attribute(declaration) end end def to_create(&block) @definition.to_create(&block) end def skip_create @definition.skip_create end def factory(name, options = {}, &block) child_factories << [name, options, block] end def trait(name, &block) @definition.define_trait(Trait.new(name, uri_paths: @definition.uri_manager.to_a, &block)) end # Creates traits for enumerable values. # # Example: # factory :task do # traits_for_enum :status, [:started, :finished] # end # # Equivalent to: # factory :task do # trait :started do # status { :started } # end # # trait :finished do # status { :finished } # end # end # # Example: # factory :task do # traits_for_enum :status, {started: 1, finished: 2} # end # # Example: # class Task # def statuses # {started: 1, finished: 2} # end # end # # factory :task do # traits_for_enum :status # end # # Both equivalent to: # factory :task do # trait :started do # status { 1 } # end # # trait :finished do # status { 2 } # end # end # # # Arguments: # attribute_name: +Symbol+ or +String+ # the name of the attribute these traits will set the value of # values: +Array+, +Hash+, or other +Enumerable+ # An array of trait names, or a mapping of trait names to values for # those traits. When this argument is not provided, factory_bot will # attempt to get the values by calling the pluralized `attribute_name` # class method. def traits_for_enum(attribute_name, values = nil) @definition.register_enum(Enum.new(attribute_name, values)) end def initialize_with(&block) @definition.define_constructor(&block) end private def __declare_attribute__(name, block) if block.nil? declaration = Declaration::Implicit.new(name, @definition, @ignore) @definition.declare_attribute(declaration) else add_attribute(name, &block) end end def __valid_association_options?(options) options.respond_to?(:has_key?) && options.has_key?(:factory) end ## # If the inline sequence has already been registered by a parent, # return that one, otherwise register and return the given sequence # def __fetch_or_register_sequence(sequence) FactoryBot::Sequence.find_by_uri(sequence.uri_manager.first) || FactoryBot::Internal.register_inline_sequence(sequence) end end end factory_bot-6.5.6/lib/factory_bot/enum.rb000066400000000000000000000011401507676517300204320ustar00rootroot00000000000000module FactoryBot # @api private class Enum def initialize(attribute_name, values = nil) @attribute_name = attribute_name @values = values end def build_traits(klass) enum_values(klass).map do |trait_name, value| build_trait(trait_name, @attribute_name, value || trait_name) end end private def enum_values(klass) @values || klass.send(@attribute_name.to_s.pluralize) end def build_trait(trait_name, attribute_name, value) Trait.new(trait_name) do add_attribute(attribute_name) { value } end end end end factory_bot-6.5.6/lib/factory_bot/errors.rb000066400000000000000000000021361507676517300210100ustar00rootroot00000000000000module FactoryBot # Raised when a factory is defined that attempts to instantiate itself. class AssociationDefinitionError < RuntimeError; end # Raised when a trait is defined that references itself. class TraitDefinitionError < RuntimeError; end # Raised when a callback is defined that has an invalid name class InvalidCallbackNameError < RuntimeError; end # Raised when a factory is defined with the same name as a previously-defined factory. class DuplicateDefinitionError < RuntimeError; end # Raised when attempting to register a sequence from a dynamic attribute block class SequenceAbuseError < RuntimeError; end # Raised when defining an attribute twice in the same factory class AttributeDefinitionError < RuntimeError; end # Raised when attempting to pass a block to an association definition class AssociationDefinitionError < RuntimeError; end # Raised when a method is defined in a factory or trait with arguments class MethodDefinitionError < RuntimeError; end # Raised when any factory is considered invalid class InvalidFactoryError < RuntimeError; end end factory_bot-6.5.6/lib/factory_bot/evaluation.rb000066400000000000000000000011061507676517300216370ustar00rootroot00000000000000module FactoryBot class Evaluation def initialize(evaluator, attribute_assigner, to_create, observer) @evaluator = evaluator @attribute_assigner = attribute_assigner @to_create = to_create @observer = observer end delegate :object, :hash, to: :@attribute_assigner def create(result_instance) case @to_create.arity when 2 then @to_create[result_instance, @evaluator] else @to_create[result_instance] end end def notify(name, result_instance) @observer.update(name, result_instance) end end end factory_bot-6.5.6/lib/factory_bot/evaluator.rb000066400000000000000000000046261507676517300215040ustar00rootroot00000000000000require "active_support/core_ext/class/attribute" module FactoryBot # @api private class Evaluator class_attribute :attribute_lists private_instance_methods.each do |method| undef_method(method) unless method.match?(/^__|initialize/) end def initialize(build_strategy, overrides = {}) @build_strategy = build_strategy @overrides = overrides @cached_attributes = overrides @instance = nil @overrides.each do |name, value| singleton_class.define_attribute(name) { value } end end def association(factory_name, *traits_and_overrides) overrides = traits_and_overrides.extract_options! strategy_override = overrides.fetch(:strategy) { FactoryBot.use_parent_strategy ? @build_strategy.to_sym : :create } traits_and_overrides += [overrides.except(:strategy)] runner = FactoryRunner.new(factory_name, strategy_override, traits_and_overrides) @build_strategy.association(runner) end attr_accessor :instance def method_missing(method_name, ...) if @instance.respond_to?(method_name) @instance.send(method_name, ...) else SyntaxRunner.new.send(method_name, ...) end end def respond_to_missing?(method_name, _include_private = false) @instance.respond_to?(method_name) || SyntaxRunner.new.respond_to?(method_name) end def __override_names__ @overrides.keys end def increment_sequence(sequence, scope: self) value = sequence.next(scope) raise if value.respond_to?(:start_with?) && value.start_with?("#)" end def self.attribute_list AttributeList.new.tap do |list| attribute_lists.each do |attribute_list| list.apply_attributes attribute_list.to_a end end end def self.define_attribute(name, &block) if instance_methods(false).include?(name) || private_instance_methods(false).include?(name) undef_method(name) end define_method(name) do if @cached_attributes.key?(name) @cached_attributes[name] else @cached_attributes[name] = instance_exec(&block) end end end end end factory_bot-6.5.6/lib/factory_bot/evaluator_class_definer.rb000066400000000000000000000010011507676517300243450ustar00rootroot00000000000000module FactoryBot # @api private class EvaluatorClassDefiner def initialize(attributes, parent_class) @parent_class = parent_class @attributes = attributes attributes.each do |attribute| evaluator_class.define_attribute(attribute.name, &attribute.to_proc) end end def evaluator_class @evaluator_class ||= Class.new(@parent_class).tap do |klass| klass.attribute_lists ||= [] klass.attribute_lists += [@attributes] end end end end factory_bot-6.5.6/lib/factory_bot/factory.rb000066400000000000000000000104631507676517300211450ustar00rootroot00000000000000require "active_support/core_ext/hash/keys" require "active_support/inflector" module FactoryBot # @api private class Factory attr_reader :name, :definition def initialize(name, options = {}) assert_valid_options(options) @name = name.respond_to?(:to_sym) ? name.to_sym : name.to_s.underscore.to_sym @parent = options[:parent] @aliases = options[:aliases] || [] @class_name = options[:class] @uri_manager = FactoryBot::UriManager.new(names) @definition = Definition.new(@name, options[:traits] || [], uri_manager: @uri_manager) @compiled = false end delegate :add_callback, :declare_attribute, :to_create, :define_trait, :constructor, :defined_traits, :defined_traits_names, :inherit_traits, :append_traits, to: :@definition def build_class @build_class ||= if class_name.is_a? Class class_name elsif class_name.to_s.safe_constantize class_name.to_s.safe_constantize else class_name.to_s.camelize.constantize end end def run(build_strategy, overrides, &block) block ||= ->(result) { result } compile strategy = Strategy.lookup_strategy(build_strategy).new evaluator = evaluator_class.new(strategy, overrides.symbolize_keys) attribute_assigner = AttributeAssigner.new(evaluator, build_class, &compiled_constructor) observer = CallbacksObserver.new(callbacks, evaluator) evaluation = Evaluation.new(evaluator, attribute_assigner, compiled_to_create, observer) evaluation.notify(:before_all, nil) instance = strategy.result(evaluation).tap(&block) evaluation.notify(:after_all, instance) instance end def human_names names.map { |name| name.to_s.humanize.downcase } end def associations evaluator_class.attribute_list.associations end # Names for this factory, including aliases. # # Example: # # factory :user, aliases: [:author] do # # ... # end # # FactoryBot.create(:author).class # # => User # # Because an attribute defined without a value or block will build an # association with the same name, this allows associations to be defined # without factories, such as: # # factory :user, aliases: [:author] do # # ... # end # # factory :post do # author # end # # FactoryBot.create(:post).author.class # # => User def names [name] + @aliases end def compile unless @compiled parent.compile inherit_parent_traits @definition.compile(build_class) build_hierarchy @compiled = true end end def with_traits(traits) clone.tap do |factory_with_traits| factory_with_traits.append_traits traits end end protected def class_name @class_name || parent.class_name || name end def evaluator_class @evaluator_class ||= EvaluatorClassDefiner.new(attributes, parent.evaluator_class).evaluator_class end def attributes compile AttributeList.new(@name).tap do |list| list.apply_attributes definition.attributes end end def hierarchy_class @hierarchy_class ||= Class.new(parent.hierarchy_class) end def hierarchy_instance @hierarchy_instance ||= hierarchy_class.new end def build_hierarchy hierarchy_class.build_from_definition definition end def callbacks hierarchy_instance.callbacks end def compiled_to_create hierarchy_instance.to_create end def compiled_constructor hierarchy_instance.constructor end private def assert_valid_options(options) options.assert_valid_keys(:class, :parent, :aliases, :traits) end def parent if @parent FactoryBot::Internal.factory_by_name(@parent) else NullFactory.new end end def inherit_parent_traits parent.defined_traits.each do |trait| next if defined_traits_names.include?(trait.name) define_trait(trait.clone) end end def initialize_copy(source) super @definition = @definition.clone @evaluator_class = nil @hierarchy_class = nil @hierarchy_instance = nil @compiled = false end end end factory_bot-6.5.6/lib/factory_bot/factory_runner.rb000066400000000000000000000014721507676517300225360ustar00rootroot00000000000000module FactoryBot class FactoryRunner def initialize(name, strategy, traits_and_overrides) @name = name @strategy = strategy @overrides = traits_and_overrides.extract_options! @traits = traits_and_overrides end def run(runner_strategy = @strategy, &block) factory = FactoryBot::Internal.factory_by_name(@name) factory.compile if @traits.any? factory = factory.with_traits(@traits) end instrumentation_payload = { name: @name, strategy: runner_strategy, traits: @traits, overrides: @overrides, factory: factory } ActiveSupport::Notifications.instrument("factory_bot.run_factory", instrumentation_payload) do factory.run(runner_strategy, @overrides, &block) end end end end factory_bot-6.5.6/lib/factory_bot/find_definitions.rb000066400000000000000000000015101507676517300230020ustar00rootroot00000000000000module FactoryBot class << self # An Array of strings specifying locations that should be searched for # factory definitions. By default, factory_bot will attempt to require # "factories.rb", "factories/**/*.rb", "test/factories.rb", # "test/factories/**.rb", "spec/factories.rb", and "spec/factories/**.rb". attr_accessor :definition_file_paths end self.definition_file_paths = %w[factories test/factories spec/factories] def self.find_definitions absolute_definition_file_paths = definition_file_paths.map { |path| File.expand_path(path) } absolute_definition_file_paths.uniq.each do |path| load("#{path}.rb") if File.exist?("#{path}.rb") if File.directory? path Dir[File.join(path, "**", "*.rb")].sort.each do |file| load file end end end end end factory_bot-6.5.6/lib/factory_bot/internal.rb000066400000000000000000000060071507676517300213110ustar00rootroot00000000000000module FactoryBot # @api private module Internal class << self delegate :after, :before, :callbacks, :constructor, :factories, :initialize_with, :inline_sequences, :sequences, :skip_create, :strategies, :to_create, :traits, to: :configuration def configuration @configuration ||= Configuration.new end def reset_configuration @configuration = nil end def register_inline_sequence(sequence) inline_sequences.push(sequence) sequence end def rewind_inline_sequences inline_sequences.each(&:rewind) end def register_trait(trait) trait.names.each do |name| traits.register(name, trait) end trait end def trait_by_name(name, klass) traits.find(name).tap { |t| t.klass = klass } end def register_sequence(sequence) sequence.names.each do |name| sequences.register(name, sequence) end sequence end def sequence_by_name(name) sequences.find(name) end def rewind_sequences sequences.each(&:rewind) rewind_inline_sequences end def rewind_sequence(*uri_parts) fail_argument_count(0, "1+") if uri_parts.empty? uri = build_uri(uri_parts) sequence = Sequence.find_by_uri(uri) || fail_unregistered_sequence(uri) sequence.rewind end def set_sequence(*uri_parts, value) uri = build_uri(uri_parts) || fail_argument_count(uri_parts.size, "2+") sequence = Sequence.find(*uri) || fail_unregistered_sequence(uri) sequence.set_value(value) end def register_factory(factory) factory.names.each do |name| factories.register(name, factory) end factory end def factory_by_name(name) factories.find(name) end def register_strategy(strategy_name, strategy_class) strategies.register(strategy_name, strategy_class) StrategySyntaxMethodRegistrar.new(strategy_name).define_strategy_methods end def strategy_by_name(name) strategies.find(name) end def register_default_strategies register_strategy(:build, FactoryBot::Strategy::Build) register_strategy(:create, FactoryBot::Strategy::Create) register_strategy(:attributes_for, FactoryBot::Strategy::AttributesFor) register_strategy(:build_stubbed, FactoryBot::Strategy::Stub) register_strategy(:null, FactoryBot::Strategy::Null) end private def build_uri(...) FactoryBot::UriManager.build_uri(...) end def fail_argument_count(received, expected) fail ArgumentError, "wrong number of arguments (given #{received}, expected #{expected})" end def fail_unregistered_sequence(uri) fail KeyError, "Sequence not registered: '#{uri}'." end end end end factory_bot-6.5.6/lib/factory_bot/linter.rb000066400000000000000000000053661507676517300210010ustar00rootroot00000000000000module FactoryBot class Linter def initialize(factories, strategy: :create, traits: false, verbose: false) @factories_to_lint = factories @factory_strategy = strategy @traits = traits @verbose = verbose @invalid_factories = calculate_invalid_factories end def lint! if invalid_factories.any? raise InvalidFactoryError, error_message end end private attr_reader :factories_to_lint, :invalid_factories, :factory_strategy def calculate_invalid_factories factories_to_lint.each_with_object(Hash.new([])) do |factory, result| errors = lint(factory) result[factory] |= errors unless errors.empty? end end class FactoryError def initialize(wrapped_error, factory) @wrapped_error = wrapped_error @factory = factory end def message message = @wrapped_error.message "* #{location} - #{message} (#{@wrapped_error.class.name})" end def verbose_message <<~MESSAGE #{message} #{@wrapped_error.backtrace.join("\n ")} MESSAGE end def location @factory.name end end class FactoryTraitError < FactoryError def initialize(wrapped_error, factory, trait_name) super(wrapped_error, factory) @trait_name = trait_name end def location "#{@factory.name}+#{@trait_name}" end end def lint(factory) if @traits lint_factory(factory) + lint_traits(factory) else lint_factory(factory) end end def lint_factory(factory) result = [] begin in_transaction { FactoryBot.public_send(factory_strategy, factory.name) } rescue => e result |= [FactoryError.new(e, factory)] end result end def lint_traits(factory) result = [] factory.definition.defined_traits.map(&:name).each do |trait_name| in_transaction { FactoryBot.public_send(factory_strategy, factory.name, trait_name) } rescue => e result |= [FactoryTraitError.new(e, factory, trait_name)] end result end def error_message lines = invalid_factories.map { |_factory, exceptions| exceptions.map(&error_message_type) }.flatten <<~ERROR_MESSAGE.strip The following factories are invalid: #{lines.join("\n")} ERROR_MESSAGE end def error_message_type if @verbose :verbose_message else :message end end def in_transaction if defined?(ActiveRecord::Base) ActiveRecord::Base.transaction do yield raise ActiveRecord::Rollback end else yield end end end end factory_bot-6.5.6/lib/factory_bot/null_factory.rb000066400000000000000000000007141507676517300221750ustar00rootroot00000000000000module FactoryBot # @api private class NullFactory attr_reader :definition def initialize @definition = Definition.new(:null_factory) end delegate :defined_traits, :callbacks, :attributes, :constructor, :to_create, to: :definition def compile end def class_name end def evaluator_class FactoryBot::Evaluator end def hierarchy_class FactoryBot::DefinitionHierarchy end end end factory_bot-6.5.6/lib/factory_bot/null_object.rb000066400000000000000000000007111507676517300217710ustar00rootroot00000000000000module FactoryBot # @api private class NullObject < ::BasicObject def initialize(methods_to_respond_to) @methods_to_respond_to = methods_to_respond_to.map(&:to_s) end def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing if respond_to?(name) nil else super end end def respond_to?(method) @methods_to_respond_to.include? method.to_s end end end factory_bot-6.5.6/lib/factory_bot/registry.rb000066400000000000000000000024611507676517300213450ustar00rootroot00000000000000require "active_support/hash_with_indifferent_access" module FactoryBot class Registry include Enumerable attr_reader :name def initialize(name) @name = name @items = ActiveSupport::HashWithIndifferentAccess.new end def clear @items.clear end def each(&block) @items.values.uniq.each(&block) end def find(name) @items.fetch(name) rescue KeyError => e raise key_error_with_custom_message(e) end alias_method :[], :find def register(name, item) @items[name] = item end def registered?(name) @items.key?(name) end private def key_error_with_custom_message(key_error) message = key_error.message.sub("key not found", "#{@name} not registered") new_key_error(message, key_error).tap do |error| error.set_backtrace(key_error.backtrace) end end # detailed_message introduced in Ruby 3.2 for cleaner integration with # did_you_mean. See https://bugs.ruby-lang.org/issues/18564 if KeyError.method_defined?(:detailed_message) def new_key_error(message, key_error) KeyError.new(message, key: key_error.key, receiver: key_error.receiver) end else def new_key_error(message, _) KeyError.new(message) end end end end factory_bot-6.5.6/lib/factory_bot/reload.rb000066400000000000000000000002151507676517300207360ustar00rootroot00000000000000module FactoryBot def self.reload Internal.reset_configuration Internal.register_default_strategies find_definitions end end factory_bot-6.5.6/lib/factory_bot/sequence.rb000066400000000000000000000107021507676517300213020ustar00rootroot00000000000000require "timeout" module FactoryBot # Sequences are defined using sequence within a FactoryBot.define block. # Sequence values are generated using next. # @api private class Sequence attr_reader :name, :uri_manager, :aliases def self.find(*uri_parts) if uri_parts.empty? fail ArgumentError, "wrong number of arguments, expected 1+)" else find_by_uri FactoryBot::UriManager.build_uri(*uri_parts) end end def self.find_by_uri(uri) uri = uri.to_sym FactoryBot::Internal.sequences.to_a.find { |seq| seq.has_uri?(uri) } || FactoryBot::Internal.inline_sequences.find { |seq| seq.has_uri?(uri) } end def initialize(name, *args, &proc) options = args.extract_options! @name = name @proc = proc @aliases = options.fetch(:aliases, []).map(&:to_sym) @uri_manager = FactoryBot::UriManager.new(names, paths: options[:uri_paths]) @value = args.first || 1 unless @value.respond_to?(:peek) @value = EnumeratorAdapter.new(@value) end end def next(scope = nil) if @proc && scope scope.instance_exec(value, &@proc) elsif @proc @proc.call(value) else value end ensure increment_value end def names [@name] + @aliases end def has_name?(test_name) names.include?(test_name.to_sym) end def has_uri?(uri) uri_manager.include?(uri) end def for_factory?(test_factory_name) FactoryBot::Internal.factory_by_name(factory_name).names.include?(test_factory_name.to_sym) end def rewind @value.rewind end ## # If it's an Integer based sequence, set the new value directly, # else rewind and seek from the beginning until a match is found. # def set_value(new_value) if can_set_value_directly?(new_value) @value.set_value(new_value) elsif can_set_value_by_index? set_value_by_index(new_value) else seek_value(new_value) end end protected attr_reader :proc private def value @value.peek end def increment_value @value.next end def can_set_value_by_index? @value.respond_to?(:find_index) end ## # Set to the given value, or fail if not found # def set_value_by_index(value) index = @value.find_index(value) || fail_value_not_found(value) @value.rewind index.times { @value.next } end ## # Rewind index and seek until the value is found or the max attempts # have been tried. If not found, the sequence is rewound to its original value # def seek_value(value) original_value = @value.peek # rewind and search for the new value @value.rewind Timeout.timeout(FactoryBot.sequence_setting_timeout) do loop do return if @value.peek == value increment_value end # loop auto-recues a StopIteration error, so if we # reached this point, re-raise it now fail StopIteration end rescue Timeout::Error, StopIteration reset_original_value(original_value) fail_value_not_found(value) end def reset_original_value(original_value) @value.rewind until @value.peek == original_value increment_value end end def can_set_value_directly?(value) return false unless value.is_a?(Integer) return false unless @value.is_a?(EnumeratorAdapter) @value.integer_value? end def fail_value_not_found(value) fail ArgumentError, "Unable to find '#{value}' in the sequence." end class EnumeratorAdapter def initialize(initial_value) @initial_value = initial_value end def peek value end def next @value = value.next end def rewind @value = first_value end def set_value(new_value) if new_value >= first_value @value = new_value else fail ArgumentError, "Value cannot be less than: #{@first_value}" end end def integer_value? first_value.is_a?(Integer) end private def first_value @first_value ||= initial_value end def value @value ||= initial_value end def initial_value @value = @initial_value.respond_to?(:call) ? @initial_value.call : @initial_value @first_value = @value end end end end factory_bot-6.5.6/lib/factory_bot/strategy.rb000066400000000000000000000006351507676517300213400ustar00rootroot00000000000000require "factory_bot/strategy/build" require "factory_bot/strategy/create" require "factory_bot/strategy/attributes_for" require "factory_bot/strategy/stub" require "factory_bot/strategy/null" module FactoryBot module Strategy def self.lookup_strategy(name_or_object) return name_or_object if name_or_object.is_a?(Class) FactoryBot::Internal.strategy_by_name(name_or_object) end end end factory_bot-6.5.6/lib/factory_bot/strategy/000077500000000000000000000000001507676517300210075ustar00rootroot00000000000000factory_bot-6.5.6/lib/factory_bot/strategy/attributes_for.rb000066400000000000000000000004041507676517300243660ustar00rootroot00000000000000module FactoryBot module Strategy class AttributesFor def association(runner) runner.run(:null) end def result(evaluation) evaluation.hash end def to_sym :attributes_for end end end end factory_bot-6.5.6/lib/factory_bot/strategy/build.rb000066400000000000000000000005571507676517300224420ustar00rootroot00000000000000module FactoryBot module Strategy class Build def association(runner) runner.run end def result(evaluation) evaluation.notify(:before_build, nil) evaluation.object.tap do |instance| evaluation.notify(:after_build, instance) end end def to_sym :build end end end end factory_bot-6.5.6/lib/factory_bot/strategy/create.rb000066400000000000000000000010021507676517300225700ustar00rootroot00000000000000module FactoryBot module Strategy class Create def association(runner) runner.run end def result(evaluation) evaluation.notify(:before_build, nil) evaluation.object.tap do |instance| evaluation.notify(:after_build, instance) evaluation.notify(:before_create, instance) evaluation.create(instance) evaluation.notify(:after_create, instance) end end def to_sym :create end end end end factory_bot-6.5.6/lib/factory_bot/strategy/null.rb000066400000000000000000000002771507676517300223140ustar00rootroot00000000000000module FactoryBot module Strategy class Null def association(runner) end def result(evaluation) end def to_sym :null end end end end factory_bot-6.5.6/lib/factory_bot/strategy/stub.rb000066400000000000000000000063501507676517300223150ustar00rootroot00000000000000module FactoryBot module Strategy class Stub @@next_id = 1000 DISABLED_PERSISTENCE_METHODS = [ :connection, :decrement!, :delete, :destroy!, :destroy, :increment!, :reload, :save!, :save, :toggle!, :touch, :update!, :update, :update_attribute, :update_attributes!, :update_attributes, :update_column, :update_columns ].freeze def self.next_id=(id) @@next_id = id end def association(runner) runner.run(:build_stubbed) end def result(evaluation) evaluation.object.tap do |instance| stub_database_interaction_on_result(instance) set_timestamps(instance) clear_changes_information(instance) evaluation.notify(:after_stub, instance) end end def to_sym :stub end private def next_id(result_instance) if uuid_primary_key?(result_instance) SecureRandom.uuid else @@next_id += 1 end end def stub_database_interaction_on_result(result_instance) if has_settable_id?(result_instance) result_instance.id ||= next_id(result_instance) end result_instance.instance_eval do def persisted? true end def new_record? false end def destroyed? false end DISABLED_PERSISTENCE_METHODS.each do |write_method| define_singleton_method(write_method) do |*args| raise "stubbed models are not allowed to access the database - " \ "#{self.class}##{write_method}(#{args.join(",")})" end end end end def has_settable_id?(result_instance) result_instance.respond_to?(:id=) && (!result_instance.class.respond_to?(:primary_key) || result_instance.class.primary_key) end def uuid_primary_key?(result_instance) result_instance.respond_to?(:column_for_attribute) && (column = result_instance.column_for_attribute(result_instance.class.primary_key)) && column.respond_to?(:sql_type) && column.sql_type == "uuid" end def clear_changes_information(result_instance) if result_instance.respond_to?(:clear_changes_information) result_instance.clear_changes_information end end def set_timestamps(result_instance) timestamp = Time.current if missing_created_at?(result_instance) result_instance.created_at = timestamp end if missing_updated_at?(result_instance) result_instance.updated_at = timestamp end end def missing_created_at?(result_instance) result_instance.respond_to?(:created_at) && result_instance.respond_to?(:created_at=) && result_instance.created_at.blank? end def missing_updated_at?(result_instance) result_instance.respond_to?(:updated_at) && result_instance.respond_to?(:updated_at=) && result_instance.updated_at.blank? end end end end factory_bot-6.5.6/lib/factory_bot/strategy_syntax_method_registrar.rb000066400000000000000000000034011507676517300263620ustar00rootroot00000000000000module FactoryBot # @api private class StrategySyntaxMethodRegistrar def initialize(strategy_name) @strategy_name = strategy_name end def define_strategy_methods define_singular_strategy_method define_list_strategy_method define_pair_strategy_method end def self.with_index(block, index) if block&.arity == 2 ->(instance) { block.call(instance, index) } else block end end private def define_singular_strategy_method strategy_name = @strategy_name define_syntax_method(strategy_name) do |name, *traits_and_overrides, &block| FactoryRunner.new(name, strategy_name, traits_and_overrides).run(&block) end end def define_list_strategy_method strategy_name = @strategy_name define_syntax_method("#{strategy_name}_list") do |name, amount, *traits_and_overrides, &block| unless amount.respond_to?(:times) raise ArgumentError, "count missing for #{strategy_name}_list" end Array.new(amount) do |i| block_with_index = StrategySyntaxMethodRegistrar.with_index(block, i) send(strategy_name, name, *traits_and_overrides, &block_with_index) end end end def define_pair_strategy_method strategy_name = @strategy_name define_syntax_method("#{strategy_name}_pair") do |name, *traits_and_overrides, &block| Array.new(2) { send(strategy_name, name, *traits_and_overrides, &block) } end end def define_syntax_method(name, &block) FactoryBot::Syntax::Methods.module_exec do if method_defined?(name) || private_method_defined?(name) undef_method(name) end define_method(name, &block) end end end end factory_bot-6.5.6/lib/factory_bot/syntax.rb000066400000000000000000000001671507676517300210240ustar00rootroot00000000000000require "factory_bot/syntax/methods" require "factory_bot/syntax/default" module FactoryBot module Syntax end end factory_bot-6.5.6/lib/factory_bot/syntax/000077500000000000000000000000001507676517300204735ustar00rootroot00000000000000factory_bot-6.5.6/lib/factory_bot/syntax/default.rb000066400000000000000000000031011507676517300224370ustar00rootroot00000000000000module FactoryBot module Syntax module Default include Methods def define(&block) DSL.run(block) end def modify(&block) ModifyDSL.run(block) end class DSL def factory(name, options = {}, &block) factory = Factory.new(name, options) proxy = FactoryBot::DefinitionProxy.new(factory.definition) proxy.instance_eval(&block) if block Internal.register_factory(factory) proxy.child_factories.each do |(child_name, child_options, child_block)| parent_factory = child_options.delete(:parent) || name factory(child_name, child_options.merge(parent: parent_factory), &child_block) end end def sequence(name, ...) Internal.register_sequence(Sequence.new(name, ...)) end def trait(name, &block) Internal.register_trait(Trait.new(name, &block)) end def self.run(block) new.instance_eval(&block) end delegate :after, :before, :callback, :initialize_with, :skip_create, :to_create, to: FactoryBot::Internal end class ModifyDSL def factory(name, _options = {}, &block) factory = Internal.factory_by_name(name) proxy = FactoryBot::DefinitionProxy.new(factory.definition.overridable) proxy.instance_eval(&block) end def self.run(block) new.instance_eval(&block) end end end end extend Syntax::Default end factory_bot-6.5.6/lib/factory_bot/syntax/methods.rb000066400000000000000000000161061507676517300224670ustar00rootroot00000000000000module FactoryBot module Syntax ## This module is a container for all strategy methods provided by ## FactoryBot. This includes all the default strategies provided ({Methods#build}, ## {Methods#create}, {Methods#build_stubbed}, and {Methods#attributes_for}), as ## well as the complementary *_list and *_pair methods. ## @example singular factory execution ## # basic use case ## build(:completed_order) ## ## # factory yielding its result to a block ## create(:post) do |post| ## create(:comment, post: post) ## end ## ## # factory with attribute override ## attributes_for(:post, title: "I love Ruby!") ## ## # factory with traits and attribute override ## build_stubbed(:user, :admin, :male, name: "John Doe") ## ## @example multiple factory execution ## # basic use case ## build_list(:completed_order, 2) ## create_list(:completed_order, 2) ## ## # factory with attribute override ## attributes_for_list(:post, 4, title: "I love Ruby!") ## ## # factory with traits and attribute override ## build_stubbed_list(:user, 15, :admin, :male, name: "John Doe") module Methods # @!parse FactoryBot::Internal.register_default_strategies # @!method build(name, *traits_and_overrides, &block) # (see #strategy_method) # Builds a registered factory by name. # @return [Object] instantiated object defined by the factory # @!method create(name, *traits_and_overrides, &block) # (see #strategy_method) # Creates a registered factory by name. # @return [Object] instantiated object defined by the factory # @!method build_stubbed(name, *traits_and_overrides, &block) # (see #strategy_method) # Builds a stubbed registered factory by name. # @return [Object] instantiated object defined by the factory # @!method attributes_for(name, *traits_and_overrides, &block) # (see #strategy_method) # Generates a hash of attributes for a registered factory by name. # @return [Hash] hash of attributes for the factory # @!method build_list(name, amount, *traits_and_overrides, &block) # (see #strategy_method_list) # @return [Array] array of built objects defined by the factory # @!method create_list(name, amount, *traits_and_overrides, &block) # (see #strategy_method_list) # @return [Array] array of created objects defined by the factory # @!method build_stubbed_list(name, amount, *traits_and_overrides, &block) # (see #strategy_method_list) # @return [Array] array of stubbed objects defined by the factory # @!method attributes_for_list(name, amount, *traits_and_overrides, &block) # (see #strategy_method_list) # @return [Array] array of attribute hashes for the factory # @!method build_pair(name, *traits_and_overrides, &block) # (see #strategy_method_pair) # @return [Array] pair of built objects defined by the factory # @!method create_pair(name, *traits_and_overrides, &block) # (see #strategy_method_pair) # @return [Array] pair of created objects defined by the factory # @!method build_stubbed_pair(name, *traits_and_overrides, &block) # (see #strategy_method_pair) # @return [Array] pair of stubbed objects defined by the factory # @!method attributes_for_pair(name, *traits_and_overrides, &block) # (see #strategy_method_pair) # @return [Array] pair of attribute hashes for the factory # @!method strategy_method(name, traits_and_overrides, &block) # @!visibility private # @param [Symbol] name the name of the factory to build # @param [Array] traits_and_overrides splat args traits and a hash of overrides # @param [Proc] block block to be executed # @!method strategy_method_list(name, amount, traits_and_overrides, &block) # @!visibility private # @param [Symbol] name the name of the factory to execute # @param [Integer] amount the number of instances to execute # @param [Array] traits_and_overrides splat args traits and a hash of overrides # @param [Proc] block block to be executed # @!method strategy_method_pair(name, traits_and_overrides, &block) # @!visibility private # @param [Symbol] name the name of the factory to execute # @param [Array] traits_and_overrides splat args traits and a hash of overrides # @param [Proc] block block to be executed # Generates and returns the next value in a global or factory sequence. # # Arguments: # context: (Array of Symbols) # The definition context of the sequence, with the sequence name # as the final entry # scope: (object)(optional) # The object the sequence should be evaluated within # # Returns: # The next value in the sequence. (Object) # # Example: # generate(:my_factory, :my_trair, :my_sequence) # def generate(*uri_parts, scope: nil) uri = FactoryBot::UriManager.build_uri(uri_parts) sequence = Sequence.find_by_uri(uri) || raise(KeyError, "Sequence not registered: #{FactoryBot::UriManager.build_uri(uri_parts)}") increment_sequence(sequence, scope: scope) end # Generates and returns the list of values in a global or factory sequence. # # Arguments: # uri_parts: (Array of Symbols) # The definition context of the sequence, with the sequence name # as the final entry # scope: (object)(optional) # The object the sequence should be evaluated within # # Returns: # The next value in the sequence. (Object) # # Example: # generate_list(:my_factory, :my_trair, :my_sequence, 5) # def generate_list(*uri_parts, count, scope: nil) uri = FactoryBot::UriManager.build_uri(uri_parts) sequence = Sequence.find_by_uri(uri) || raise(KeyError, "Sequence not registered: '#{uri}'") (1..count).map do increment_sequence(sequence, scope: scope) end end # ====================================================================== # = PRIVATE # ====================================================================== # private ## # Increments the given sequence and returns the value. # # Arguments: # sequence: # The sequence instance # scope: (object)(optional) # The object the sequence should be evaluated within # def increment_sequence(sequence, scope: nil) value = sequence.next(scope) raise if value.respond_to?(:start_with?) && value.start_with?("#)" end end end end factory_bot-6.5.6/lib/factory_bot/syntax_runner.rb000066400000000000000000000001361507676517300224110ustar00rootroot00000000000000module FactoryBot # @api private class SyntaxRunner include Syntax::Methods end end factory_bot-6.5.6/lib/factory_bot/trait.rb000066400000000000000000000015621507676517300206210ustar00rootroot00000000000000module FactoryBot # @api private class Trait attr_reader :name, :uid, :definition delegate :add_callback, :declare_attribute, :to_create, :define_trait, :constructor, :callbacks, :attributes, :klass, :klass=, to: :@definition def initialize(name, **options, &block) @name = name.to_s @block = block @uri_manager = FactoryBot::UriManager.new(names, paths: options[:uri_paths]) @definition = Definition.new(@name, uri_manager: @uri_manager) proxy = FactoryBot::DefinitionProxy.new(@definition) if block proxy.instance_eval(&@block) end end def clone Trait.new(name, uri_paths: definition.uri_manager.paths, &block) end def names [@name] end def ==(other) name == other.name && block == other.block end protected attr_reader :block end end factory_bot-6.5.6/lib/factory_bot/uri_manager.rb000066400000000000000000000027131507676517300217660ustar00rootroot00000000000000module FactoryBot # @api private class UriManager attr_reader :endpoints, :paths, :uri_list delegate :size, :any?, :empty?, :each?, :include?, :first, to: :@uri_list delegate :build_uri, to: :class # Concatenate the parts, sripping leading/following slashes # and returning a Symbolized String or nil. # # Example: # build_uri(:my_factory, :my_trait, :my_sequence) # #=> :"myfactory/my_trait/my_sequence" # def self.build_uri(*parts) return nil if parts.empty? parts.join("/") .sub(/\A\/+/, "") .sub(/\/+\z/, "") .tr(" ", "_") .to_sym end # Configures the new UriManager # # Arguments: # endpoints: (Array of Strings or Symbols) # the objects endpoints. # # paths: (Array of Strings or Symbols) # the parent URIs to prepend to each endpoint # def initialize(*endpoints, paths: []) if endpoints.empty? fail ArgumentError, "wrong number of arguments (given 0, expected 1+)" end @uri_list = [] @endpoints = endpoints.flatten @paths = Array(paths).flatten build_uri_list end def to_a @uri_list.dup end private def build_uri_list @endpoints.each do |endpoint| if @paths.any? @paths.each { |path| @uri_list << build_uri(path, endpoint) } else @uri_list << build_uri(endpoint) end end end end end factory_bot-6.5.6/lib/factory_bot/version.rb000066400000000000000000000000611507676517300211540ustar00rootroot00000000000000module FactoryBot VERSION = "6.5.6".freeze end factory_bot-6.5.6/spec/000077500000000000000000000000001507676517300150165ustar00rootroot00000000000000factory_bot-6.5.6/spec/acceptance/000077500000000000000000000000001507676517300171045ustar00rootroot00000000000000factory_bot-6.5.6/spec/acceptance/activesupport_instrumentation_spec.rb000066400000000000000000000146141507676517300267040ustar00rootroot00000000000000unless ActiveSupport::Notifications.respond_to?(:subscribed) module SubscribedBehavior def subscribed(callback, *args) subscriber = subscribe(*args, &callback) yield ensure unsubscribe(subscriber) end end ActiveSupport::Notifications.extend SubscribedBehavior end describe "using ActiveSupport::Instrumentation to track run_factory interaction" do let(:slow_user_factory) { FactoryBot::Internal.factory_by_name("slow_user") } let(:user_factory) { FactoryBot::Internal.factory_by_name("user") } before do define_model("User", email: :string) define_model("Post", user_id: :integer) do belongs_to :user end FactoryBot.define do factory :user do email { "john@example.com" } factory :slow_user do after(:build) { Kernel.sleep(0.1) } end end factory :post do trait :with_user do user end end end end it "tracks proper time of creating the record" do time_to_execute = 0 callback = ->(_name, start, finish, _id, _payload) { time_to_execute = finish - start } ActiveSupport::Notifications.subscribed(callback, "factory_bot.run_factory") do FactoryBot.build(:slow_user) end expect(time_to_execute).to be >= 0.1 end it "builds the correct payload", :slow do tracked_invocations = {} callback = ->(_name, _start, _finish, _id, payload) do factory_name = payload[:name] strategy_name = payload[:strategy] factory = payload[:factory] tracked_invocations[factory_name] ||= {} tracked_invocations[factory_name][strategy_name] ||= 0 tracked_invocations[factory_name][strategy_name] += 1 tracked_invocations[factory_name][:factory] = factory end ActiveSupport::Notifications.subscribed(callback, "factory_bot.run_factory") do FactoryBot.build_list(:slow_user, 2) FactoryBot.build_list(:user, 5) FactoryBot.create_list(:user, 2) FactoryBot.attributes_for(:slow_user) user = FactoryBot.create(:user) FactoryBot.create(:post, user: user) FactoryBot.create_list(:post, 2, :with_user) end expect(tracked_invocations[:slow_user][:build]).to eq(2) expect(tracked_invocations[:slow_user][:attributes_for]).to eq(1) expect(tracked_invocations[:slow_user][:factory]).to eq(slow_user_factory) expect(tracked_invocations[:user][:build]).to eq(5) expect(tracked_invocations[:user][:factory]).to eq(user_factory) expect(tracked_invocations[:user][:create]).to eq(5) end end describe "using ActiveSupport::Instrumentation to track compile_factory interaction" do before do define_model("User", name: :string, email: :string) FactoryBot.define do factory :user do sequence(:email) { |n| "user_#{n}@example.com" } name { "User" } trait :special do name { "Special User" } end end end end it "tracks proper time of compiling the factory" do time_to_execute = {user: 0} callback = ->(_name, start, finish, _id, payload) { time_to_execute[payload[:name]] = (finish - start) } ActiveSupport::Notifications.subscribed(callback, "factory_bot.compile_factory") do FactoryBot.build(:user) end expect(time_to_execute[:user]).to be > 0 end it "builds the correct payload" do tracked_payloads = [] callback = ->(_name, _start, _finish, _id, payload) { tracked_payloads << payload } ActiveSupport::Notifications.subscribed(callback, "factory_bot.compile_factory") do FactoryBot.build(:user, :special) end factory_payload = tracked_payloads.detect { |payload| payload[:name] == :user } expect(factory_payload[:class]).to eq(User) expect(factory_payload[:attributes].map(&:name)).to eq([:email, :name]) expect(factory_payload[:traits].map(&:name)).to eq(["special"]) trait_payload = tracked_payloads.detect { |payload| payload[:name] == "special" } expect(trait_payload[:class]).to eq(User) expect(trait_payload[:attributes].map(&:name)).to eq([:name]) expect(trait_payload[:traits].map(&:name)).to eq(["special"]) end context "when factory with base traits" do before do define_model("Company", name: :string, email: :string) FactoryBot.define do trait :email do email { "#{name}@example.com" } end factory :company, traits: [:email] do name { "Charlie" } end end end it "builds the correct payload" do tracked_payloads = [] callback = ->(_name, _start, _finish, _id, payload) { tracked_payloads << payload } ActiveSupport::Notifications.subscribed(callback, "factory_bot.compile_factory") do FactoryBot.build(:company) end factory_payload = tracked_payloads.detect { |payload| payload[:name] == :company } expect(factory_payload[:class]).to eq(Company) expect(factory_payload[:attributes].map(&:name)).to eq([:name]) expect(factory_payload[:traits].map(&:name)).to eq([]) trait_payload = tracked_payloads.detect { |payload| payload[:name] == "email" } expect(trait_payload[:class]).to eq(Company) expect(trait_payload[:attributes].map(&:name)).to eq([:email]) expect(trait_payload[:traits].map(&:name)).to eq([]) end end context "when factory with additional traits" do before do define_model("Company", name: :string, email: :string) FactoryBot.define do trait :email do email { "#{name}@example.com" } end factory :company do name { "Charlie" } end end end it "builds the correct payload" do tracked_payloads = [] callback = ->(_name, _start, _finish, _id, payload) { tracked_payloads << payload } ActiveSupport::Notifications.subscribed(callback, "factory_bot.compile_factory") do FactoryBot.build(:company, :email) end factory_payload = tracked_payloads.detect { |payload| payload[:name] == :company } expect(factory_payload[:class]).to eq(Company) expect(factory_payload[:attributes].map(&:name)).to eq([:name]) expect(factory_payload[:traits].map(&:name)).to eq([]) trait_payload = tracked_payloads.detect { |payload| payload[:name] == "email" } expect(trait_payload[:class]).to eq(Company) expect(trait_payload[:attributes].map(&:name)).to eq([:email]) expect(trait_payload[:traits].map(&:name)).to eq([]) end end end factory_bot-6.5.6/spec/acceptance/add_attribute_spec.rb000066400000000000000000000017761507676517300232710ustar00rootroot00000000000000describe "#add_attribute" do it "assigns attributes for reserved words on .build" do define_model("Post", title: :string, sequence: :string, new: :boolean) FactoryBot.define do factory :post do add_attribute(:title) { "Title" } add_attribute(:sequence) { "Sequence" } add_attribute(:new) { true } end end post = FactoryBot.build(:post) expect(post.title).to eq "Title" expect(post.sequence).to eq "Sequence" expect(post.new).to eq true end it "assigns attributes for reserved words on .attributes_for" do define_model("Post", title: :string, sequence: :string, new: :boolean) FactoryBot.define do factory :post do add_attribute(:title) { "Title" } add_attribute(:sequence) { "Sequence" } add_attribute(:new) { true } end end post = FactoryBot.attributes_for(:post) expect(post[:title]).to eq "Title" expect(post[:sequence]).to eq "Sequence" expect(post[:new]).to eq true end end factory_bot-6.5.6/spec/acceptance/associations_spec.rb000066400000000000000000000077441507676517300231560ustar00rootroot00000000000000describe "associations" do context "when accidentally using an implicit declaration for the factory" do it "raises an error" do define_class("Post") FactoryBot.define do factory :post do author factory: user end end expect { FactoryBot.build(:post) }.to raise_error( ArgumentError, "Association 'author' received an invalid factory argument.\n" \ "Did you mean? 'factory: :user'\n" ) end end context "when accidentally using an implicit declaration as an override" do it "raises an error" do define_class("Post") FactoryBot.define do factory :post do author factory: :user, invalid_attribute: implicit_trait end end expect { FactoryBot.build(:post) }.to raise_error( ArgumentError, "Association 'author' received an invalid attribute override.\n" \ "Did you mean? 'invalid_attribute: :implicit_trait'\n" ) end end context "when building interrelated associations" do it "assigns the instance passed as an association attribute" do define_class("Supplier") do attr_accessor :account end define_class("Account") do attr_accessor :supplier end FactoryBot.define do factory :supplier factory :account do supplier { association(:supplier, account: instance) } end end account = FactoryBot.build(:account) expect(account.supplier.account).to eq(account) end it "connects records with interdependent relationships" do define_model("Student", school_id: :integer) do belongs_to :school has_one :profile end define_model("Profile", school_id: :integer, student_id: :integer) do belongs_to :school belongs_to :student end define_model("School") do has_many :students has_many :profiles end FactoryBot.define do factory :student do school profile { association :profile, student: instance, school: school } end factory :profile do school student { association :student, profile: instance, school: school } end factory :school end student = FactoryBot.create(:student) expect(student.profile.school).to eq(student.school) expect(student.profile.student).to eq(student) expect(student.school.students.map(&:id)).to eq([student.id]) expect(student.school.profiles.map(&:id)).to eq([student.profile.id]) profile = FactoryBot.create(:profile) expect(profile.student.school).to eq(profile.school) expect(profile.student.profile).to eq(profile) expect(profile.school.profiles.map(&:id)).to eq([profile.id]) expect(profile.school.students.map(&:id)).to eq([profile.student.id]) end end context "when building collection associations" do it "builds the association according to the given strategy" do define_model("Photo", listing_id: :integer) do belongs_to :listing attr_accessor :name end define_model("Listing") do has_many :photos end FactoryBot.define do factory :photo factory :listing do photos { [association(:photo)] } end end created_listing = FactoryBot.create(:listing) expect(created_listing.photos.first).to be_a Photo expect(created_listing.photos.first).to be_persisted built_listing = FactoryBot.build(:listing) expect(built_listing.photos.first).to be_a Photo expect(built_listing.photos.first).not_to be_persisted stubbed_listing = FactoryBot.build_stubbed(:listing) expect(stubbed_listing.photos.first).to be_a Photo expect(stubbed_listing.photos.first).to be_persisted expect { stubbed_listing.photos.first.save! }.to raise_error( "stubbed models are not allowed to access the database - Photo#save!()" ) end end end factory_bot-6.5.6/spec/acceptance/attribute_aliases_spec.rb000066400000000000000000000324431507676517300241550ustar00rootroot00000000000000describe "attribute aliases" do around do |example| original_aliases = FactoryBot.aliases.dup example.run ensure FactoryBot.aliases.clear FactoryBot.aliases.concat(original_aliases) end describe "basic alias functionality" do it "allows using different parameter names that map to model attributes" do FactoryBot.aliases << [/author_name/, "name"] define_model("User", name: :string, author_name: :string) FactoryBot.define do factory :user do name { "Default Name" } end end user = FactoryBot.create(:user, author_name: "Custom Name") expect(user.author_name).to eq "Custom Name" expect(user.name).to be_nil end it "ignores factory defaults when alias is used" do FactoryBot.aliases << [/display_name/, "name"] define_model("User", name: :string, display_name: :string) FactoryBot.define do factory :user do name { "Factory Name" } end end user = FactoryBot.create(:user, display_name: "Override Name") expect(user.display_name).to eq "Override Name" expect(user.name).to be_nil end end describe "built-in _id aliases" do it "automatically alias between associations and foreign keys" do define_model("User", name: :string) define_model("Post", user_id: :integer) do belongs_to :user, optional: true end FactoryBot.define do factory :user do name { "Test User" } end factory :post end user = FactoryBot.create(:user) post_with_direct_id = FactoryBot.create(:post, user_id: user.id) expect(post_with_direct_id.user_id).to eq user.id post_with_association = FactoryBot.create(:post, user: user) expect(post_with_association.user_id).to eq user.id end it "prevents conflicts between associations and foreign keys" do define_model("User", name: :string, age: :integer) define_model("Post", user_id: :integer) do belongs_to :user end FactoryBot.define do factory :user do factory :user_with_name do name { "John Doe" } end end factory :post do user end end post_with_foreign_key = FactoryBot.build(:post, user_id: 1) expect(post_with_foreign_key.user_id).to eq 1 end it "allows passing attributes to associated factories" do define_model("User", name: :string, age: :integer) define_model("Post", user_id: :integer) do belongs_to :user end FactoryBot.define do factory :user do factory :user_with_name do name { "John Doe" } end end factory :post do user end factory :post_with_named_user, class: Post do user factory: :user_with_name, age: 20 end end created_user = FactoryBot.create(:post_with_named_user).user expect(created_user.name).to eq "John Doe" expect(created_user.age).to eq 20 end end describe "custom alias patterns" do it "supports regex patterns with capture groups" do FactoryBot.aliases << [/(.+)_alias/, '\1'] define_model("User", name: :string, name_alias: :string, email: :string, email_alias: :string) FactoryBot.define do factory :user do name { "Default Name" } email { "default@example.com" } end end user = FactoryBot.create(:user, name_alias: "Aliased Name", email_alias: "aliased@example.com") expect(user.name).to be_nil expect(user.email).to be_nil expect(user.name_alias).to eq "Aliased Name" expect(user.email_alias).to eq "aliased@example.com" end it "supports multiple alias patterns working together" do FactoryBot.aliases << [/primary_(.+)/, '\1'] FactoryBot.aliases << [/alt_name/, "name"] define_model("User", name: :string, email: :string, primary_email: :string, alt_name: :string) FactoryBot.define do factory :user do name { "Default Name" } email { "default@example.com" } end end user = FactoryBot.create(:user, primary_email: "primary@example.com", alt_name: "Alternative Name") expect(user.name).to be_nil expect(user.email).to be_nil expect(user.primary_email).to eq "primary@example.com" expect(user.alt_name).to eq "Alternative Name" end it "works with custom foreign key names" do FactoryBot.aliases << [/owner_id/, "user_id"] define_model("User", name: :string) define_model("Document", user_id: :integer, owner_id: :integer, title: :string) do belongs_to :user, optional: true end FactoryBot.define do factory :user do name { "Test User" } end factory :document do title { "Test Document" } end end document_owner = FactoryBot.create(:user) document = FactoryBot.create(:document, owner_id: document_owner.id) expect(document.user_id).to be_nil expect(document.owner_id).to eq document_owner.id expect(document.title).to eq "Test Document" end end describe "edge cases" do it "allows setting nil values through aliases" do FactoryBot.aliases << [/clear_name/, "name"] define_model("User", name: :string, clear_name: :string) FactoryBot.define do factory :user do name { "Default Name" } end end user = FactoryBot.create(:user, clear_name: nil) expect(user.name).to be_nil expect(user.clear_name).to be_nil end context "when both alias and target attributes exist on model" do it "ignores factory defaults for target when alias is used" do FactoryBot.aliases << [/one/, "two"] define_model("User", two: :string, one: :string) FactoryBot.define do factory :user do two { "set value" } end end user = FactoryBot.create(:user, one: "override") expect(user.one).to eq "override" expect(user.two).to be_nil end end end describe "with attributes_for strategy" do it "includes alias names in hash and ignores aliased factory defaults" do FactoryBot.aliases << [/author_name/, "name"] define_model("User", name: :string, author_name: :string) FactoryBot.define do factory :user do name { "Default Name" } end end attributes = FactoryBot.attributes_for(:user, author_name: "Custom Name") expect(attributes[:author_name]).to eq "Custom Name" expect(attributes[:name]).to be_nil end end describe "attribute conflicts with _id patterns" do it "doesn't set factory defaults when alias is used instead of target attribute" do define_model("User", name: :string, response_id: :integer) FactoryBot.define do factory :user do name { "orig name" } response_id { 42 } end end attributes = FactoryBot.attributes_for(:user, name: "new name", response: 13.75) expect(attributes[:name]).to eq "new name" expect(attributes[:response]).to eq 13.75 expect(attributes[:response_id]).to be_nil end it "allows setting both attribute and attribute_id without conflicts" do define_model("User", name: :string, response: :string, response_id: :float) FactoryBot.define do factory :user do name { "orig name" } response { "orig response" } response_id { 42 } end end user = FactoryBot.build(:user, name: "new name", response: "new response", response_id: 13.75) expect(user.name).to eq "new name" expect(user.response).to eq "new response" expect(user.response_id).to eq 13.75 end context "when association overrides trait foreign key" do before do define_model("User", name: :string) define_model("Post", user_id: :integer, title: :string) do belongs_to :user, optional: true end FactoryBot.define do factory :user do name { "Test User" } end factory :post do association :user title { "Test Post" } trait :with_system_user_id do user_id { 999 } end trait :with_user_id_100 do user_id { 100 } end trait :with_user_id_200 do user_id { 200 } end end end end it "prefers association override over trait foreign key" do user = FactoryBot.create(:user) post = FactoryBot.build(:post, :with_system_user_id, user: user) expect(post.user).to be user expect(post.user_id).to eq user.id end it "uses trait foreign key when no association override is provided" do post = FactoryBot.build(:post, :with_system_user_id) expect(post.user).to be nil expect(post.user_id).to eq 999 end it "handles multiple traits with foreign keys correctly" do user = FactoryBot.create(:user) post = FactoryBot.build(:post, :with_user_id_100, :with_user_id_200, user: user) expect(post.user).to be user expect(post.user_id).to eq user.id end end end context "when a factory defines attributes for both sides of an association" do before do define_model("User", name: :string, age: :integer) define_model("Post", user_id: :integer, title: :string) do belongs_to :user end end context "when using the build strategy" do it "prefers the :user association when defined after the :user_id attribute" do FactoryBot.define do factory :user factory :post do user_id { 999 } user end end user = FactoryBot.build_stubbed(:user) post = FactoryBot.build(:post, user_id: user.id) # A regression from v6.5.5 resulted in an extraneous user instance being # built and assigned to post.user; it also failed to use the user_id override expect(post.user).to be nil expect(post.user_id).to eq user.id end it "prefers the :user_id attribute when defined after the :user attribute" do FactoryBot.define do factory :user factory :post do user user_id { 999 } end end user = FactoryBot.build_stubbed(:user) post = FactoryBot.build(:post, user: user) # A regression from v6.5.5 erroneously assigns the value of 999 to post.user_id # and fails to assign the user override expect(post.user).to be user expect(post.user_id).to be user.id end end context "when using the create strategy" do it "handles an override of the foreign key when the :user association is declared last" do FactoryBot.define do factory :user factory :post do user_id { 999 } user end end user = FactoryBot.create(:user) post = FactoryBot.create(:post, user_id: user.id) # A regression in v6.5.5 created an erroneous second user and assigned # that to post.user and post.user_id. expect(post.user.id).to be user.id expect(post.user_id).to eq user.id expect(User.count).to eq 1 end it "handles an override of the associated object when the :user association is declared last" do FactoryBot.define do factory :user factory :post do user_id { 999 } user end end user = FactoryBot.create(:user) post = FactoryBot.create(:post, user: user) # This worked fine in v6.5.5, no regression behavior exhibited expect(post.user).to eq user expect(post.user_id).to eq user.id expect(User.count).to eq 1 end it "handles an override of the associated object when :user_id is declared last" do FactoryBot.define do factory :user factory :post do user # this :user_id attribute is purposely declared after :user user_id { 999 } end end user = FactoryBot.create(:user) post = FactoryBot.create(:post, user: user) # A regression from v6.5.5 erroneously asignes 999 to post.user_id # and leaves post.user assigned to nil expect(post.user_id).to eq user.id expect(post.user).to eq user expect(User.count).to eq 1 end it "handles an override of the foreign key when :user_id is declared last" do FactoryBot.define do factory :user do name { "tester" } age { 99 } end factory :post do user # this :user_id attribute is purposely declared after :user user_id { 999 } end end user = FactoryBot.create(:user) post = FactoryBot.create(:post, user_id: user.id) # A regression from v6.5.5 assigns the expected values to post.user and post.user_id # An erroneous second user instance, however, is created in the background expect(post.user_id).to eq user.id expect(post.user.id).to be user.id expect(User.count).to eq 1 end end end end factory_bot-6.5.6/spec/acceptance/attribute_existing_on_object_spec.rb000066400000000000000000000033351507676517300264060ustar00rootroot00000000000000describe "declaring attributes on a Factory that are private methods on Object" do before do define_model("Website", system: :boolean, link: :string, sleep: :integer) FactoryBot.define do factory :website do system { false } link { "http://example.com" } sleep { 15 } end end end subject { FactoryBot.build(:website, sleep: -5) } its(:system) { should eq false } its(:link) { should eq "http://example.com" } its(:sleep) { should eq(-5) } end describe "assigning overrides that are also private methods on object" do before do define_model("Website", format: :string, y: :integer, more_format: :string, some_funky_method: :string) Object.class_eval do private def some_funky_method(args) end end FactoryBot.define do factory :website do more_format { "format: #{format}" } end end end after do Object.send(:undef_method, :some_funky_method) end subject { FactoryBot.build(:website, format: "Great", y: 12345, some_funky_method: "foobar!") } its(:format) { should eq "Great" } its(:y) { should eq 12345 } its(:more_format) { should eq "format: Great" } its(:some_funky_method) { should eq "foobar!" } end describe "accessing methods from the instance within a dynamic attribute " \ "that is also a private method on object" do before do define_model("Website", more_format: :string) do def format "This is an awesome format" end end FactoryBot.define do factory :website do more_format { "format: #{format}" } end end end subject { FactoryBot.build(:website) } its(:more_format) { should eq "format: This is an awesome format" } end factory_bot-6.5.6/spec/acceptance/attributes_for_destructuring.rb000066400000000000000000000011611507676517300254460ustar00rootroot00000000000000describe "Ruby 3.0: attributes_for destructuring syntax" do include FactoryBot::Syntax::Methods before do define_model("User", name: :string) FactoryBot.define do factory :user do sequence(:email) { "email_#{_1}@example.com" } name { "John Doe" } end end end it "supports being destructured" do # rubocop:disable Lint/Syntax attributes_for(:user) => {name:, **attributes} # rubocop:enable Lint/Syntax expect(name).to eq("John Doe") expect(attributes.keys).to eq([:email]) expect(attributes.fetch(:email)).to match(/email_\d+@example.com/) end end factory_bot-6.5.6/spec/acceptance/attributes_for_spec.rb000066400000000000000000000042651507676517300235060ustar00rootroot00000000000000if RUBY_ENGINE != "truffleruby" require_relative "attributes_for_destructuring" end describe "a generated attributes hash" do include FactoryBot::Syntax::Methods before do define_model("User") define_model("Comment") define_model("Post", title: :string, body: :string, summary: :string, user_id: :integer) do belongs_to :user has_many :comments end FactoryBot.define do factory :user factory :comment factory :post do title { "default title" } body { "default body" } summary { title } user comments do |c| [c.association(:comment)] end end end end subject { attributes_for(:post, title: "overridden title") } it "assigns an overridden value" do expect(subject[:title]).to eq "overridden title" end it "assigns a default value" do expect(subject[:body]).to eq "default body" end it "assigns a lazy, dependent attribute" do expect(subject[:summary]).to eq "overridden title" end it "doesn't assign associations" do expect(subject).not_to have_key(:user_id) expect(subject).not_to have_key(:user) end end describe "calling `attributes_for` with a block" do include FactoryBot::Syntax::Methods before do define_model("Company", name: :string) FactoryBot.define do factory :company end end it "passes the hash of attributes" do attributes_for(:company, name: "thoughtbot") do |attributes| expect(attributes[:name]).to eq("thoughtbot") end end it "returns the hash of attributes" do expected = nil result = attributes_for(:company) { |attributes| expected = attributes "hello!" } expect(result).to eq expected end end describe "`attributes_for` for a class whose constructor has required params" do before do define_model("User", name: :string) do def initialize(arg1, arg2) # Constructor requesting params to be used for testing end end FactoryBot.define do factory :user do name { "John Doe" } end end end subject { FactoryBot.attributes_for(:user) } its([:name]) { should eq "John Doe" } end factory_bot-6.5.6/spec/acceptance/attributes_from_instance_spec.rb000066400000000000000000000026431507676517300255450ustar00rootroot00000000000000describe "calling methods on the model instance" do before do define_model("User", age: :integer, age_copy: :integer) do def age read_attribute(:age) || 18 end end FactoryBot.define do factory :user do age_copy { age } end end end context "without the attribute being overridden" do it "returns the correct value from the instance" do expect(FactoryBot.build(:user).age_copy).to eq 18 end it "returns nil during attributes_for" do expect(FactoryBot.attributes_for(:user)[:age_copy]).to be_nil end it "doesn't instantiate a record with attributes_for" do allow(User).to receive(:new) FactoryBot.attributes_for(:user) expect(User).to_not have_received(:new) end end context "with the attribute being overridden" do it "uses the overridden value" do expect(FactoryBot.build(:user, age_copy: nil).age_copy).to be_nil end it "uses the overridden value during attributes_for" do expect(FactoryBot.attributes_for(:user, age_copy: 25)[:age_copy]).to eq 25 end end context "with the referenced attribute being overridden" do it "uses the overridden value" do expect(FactoryBot.build(:user, age: nil).age_copy).to be_nil end it "uses the overridden value during attributes_for" do expect(FactoryBot.attributes_for(:user, age: 25)[:age_copy]).to eq 25 end end end factory_bot-6.5.6/spec/acceptance/attributes_ordered_spec.rb000066400000000000000000000025561507676517300243450ustar00rootroot00000000000000describe "a generated attributes hash where order matters" do include FactoryBot::Syntax::Methods before do define_model("ParentModel", static: :integer, evaluates_first: :integer, evaluates_second: :integer, evaluates_third: :integer) FactoryBot.define do factory :parent_model do evaluates_first { static } evaluates_second { evaluates_first } evaluates_third { evaluates_second } factory :child_model do static { 1 } end end factory :without_parent, class: ParentModel do evaluates_first { static } evaluates_second { evaluates_first } evaluates_third { evaluates_second } static { 1 } end end end context "factory with a parent" do subject { FactoryBot.build(:child_model) } it "assigns attributes in the order they're defined" do expect(subject[:evaluates_first]).to eq 1 expect(subject[:evaluates_second]).to eq 1 expect(subject[:evaluates_third]).to eq 1 end end context "factory without a parent" do subject { FactoryBot.build(:without_parent) } it "assigns attributes in the order they're defined without a parent class" do expect(subject[:evaluates_first]).to eq 1 expect(subject[:evaluates_second]).to eq 1 expect(subject[:evaluates_third]).to eq 1 end end end factory_bot-6.5.6/spec/acceptance/build_list_spec.rb000066400000000000000000000032461507676517300226020ustar00rootroot00000000000000describe "build multiple instances" do before do define_model("Post", title: :string, position: :integer) FactoryBot.define do factory(:post) do |post| post.title { "Through the Looking Glass" } post.position { rand(10**4) } end end end context "without default attributes" do subject { FactoryBot.build_list(:post, 20) } its(:length) { should eq 20 } it "builds (but doesn't save) all the posts" do subject.each do |record| expect(record).to be_new_record end end it "uses the default factory values" do subject.each do |record| expect(record.title).to eq "Through the Looking Glass" end end end context "with default attributes" do subject { FactoryBot.build_list(:post, 20, title: "The Hunting of the Snark") } it "overrides the default values" do subject.each do |record| expect(record.title).to eq "The Hunting of the Snark" end end end context "with a block" do subject do FactoryBot.build_list(:post, 20, title: "The Listing of the Block") do |post| post.position = post.id end end it "correctly uses the set value" do subject.each do |record| expect(record.position).to eq record.id end end end context "with a block that receives both the object and an index" do subject do FactoryBot.build_list(:post, 20, title: "The Indexed Block") do |post, index| post.position = index end end it "correctly uses the set value" do subject.each_with_index do |record, index| expect(record.position).to eq index end end end end factory_bot-6.5.6/spec/acceptance/build_spec.rb000066400000000000000000000040461507676517300215460ustar00rootroot00000000000000describe "a built instance" do include FactoryBot::Syntax::Methods before do define_model("User") define_model("Post", user_id: :integer) do belongs_to :user end FactoryBot.define do factory :user factory :post do user end end end subject { build(:post) } it { should be_new_record } context "when the :use_parent_strategy config option is set to false" do it "assigns and saves associations" do with_temporary_assignment(FactoryBot, :use_parent_strategy, false) do expect(subject.user).to be_kind_of(User) expect(subject.user).not_to be_new_record end end end context "when the :use_parent_strategy config option is set to true" do it "assigns but does not save associations" do with_temporary_assignment(FactoryBot, :use_parent_strategy, true) do expect(subject.user).to be_kind_of(User) expect(subject.user).to be_new_record end end end end describe "a built instance with strategy: :create" do include FactoryBot::Syntax::Methods before do define_model("User") define_model("Post", user_id: :integer) do belongs_to :user end FactoryBot.define do factory :user factory :post do association(:user, strategy: :create) end end end subject { build(:post) } it { should be_new_record } it "assigns and saves associations" do expect(subject.user).to be_kind_of(User) expect(subject.user).not_to be_new_record end end describe "calling `build` with a block" do include FactoryBot::Syntax::Methods before do define_model("Company", name: :string) FactoryBot.define do factory :company end end it "passes the built instance" do build(:company, name: "thoughtbot") do |company| expect(company.name).to eq("thoughtbot") end end it "returns the built instance" do expected = nil result = build(:company) { |company| expected = company "hello!" } expect(result).to eq expected end end factory_bot-6.5.6/spec/acceptance/build_stubbed_spec.rb000066400000000000000000000164561507676517300232660ustar00rootroot00000000000000describe "a generated stub instance" do include FactoryBot::Syntax::Methods before do define_model("User") define_model("Post", title: :string, body: :string, age: :integer, user_id: :integer, draft: :boolean) do belongs_to :user end FactoryBot.define do factory :user factory :post do title { "default title" } body { "default body" } user end end end subject { build_stubbed(:post, title: "overridden title") } it "assigns a default attribute" do expect(subject.body).to eq "default body" end it "assigns an overridden attribute" do expect(subject.title).to eq "overridden title" end it "assigns associations" do expect(subject.user).to be_kind_of(User) end it "has an id" do expect(subject.id).to be > 0 end it "generates unique ids" do other_stub = build_stubbed(:post) expect(subject.id).not_to eq other_stub.id end it "isn't a new record" do expect(subject).not_to be_new_record end it "assigns associations that aren't new records" do expect(subject.user).not_to be_new_record end it "isn't changed" do expect(subject).not_to be_changed end it "disables connection" do expect { subject.connection }.to raise_error(RuntimeError) end it "disables update_attribute" do expect { subject.update_attribute(:title, "value") }.to raise_error(RuntimeError) end it "disables reload" do expect { subject.reload }.to raise_error(RuntimeError) end it "disables destroy" do expect { subject.destroy }.to raise_error(RuntimeError) end it "disables save" do expect { subject.save }.to raise_error(RuntimeError) end it "disables increment!" do expect { subject.increment!(:age) }.to raise_error(RuntimeError) end it "disables decrement!" do expect { subject.decrement!(:age) }.to raise_error(RuntimeError) end it "disables toggle!" do expect { subject.toggle!(:draft) }.to raise_error(RuntimeError) end it "allows increment" do subject.age = 1 subject.increment(:age) expect(subject.age).to eq(2) end it "allows decrement" do subject.age = 1 subject.decrement(:age) expect(subject.age).to eq(0) end it "allows toggle" do subject.draft = true subject.toggle(:draft) expect(subject).not_to be_draft end end describe "calling `build_stubbed` with a block" do include FactoryBot::Syntax::Methods before do define_model("Company", name: :string) FactoryBot.define do factory :company end end it "passes the stub instance" do build_stubbed(:company, name: "thoughtbot") do |company| expect(company.name).to eq("thoughtbot") expect { company.save }.to raise_error(RuntimeError) end end it "returns the stub instance" do expected = nil result = build_stubbed(:company) { |company| expected = company "hello!" } expect(result).to eq expected end end describe "defaulting `created_at`" do include FactoryBot::Syntax::Methods before do define_model("ThingWithTimestamp", created_at: :datetime) define_model("ThingWithoutTimestamp") FactoryBot.define do factory :thing_with_timestamp factory :thing_without_timestamp end end it "defaults created_at for objects with created_at" do expect(build_stubbed(:thing_with_timestamp).created_at).to be_about_now end it "is doesn't mark the object as changed" do stub = build_stubbed(:thing_with_timestamp) expect(stub).not_to be_changed end it "doesn't add created_at to objects who don't have the method" do expect(build_stubbed(:thing_without_timestamp)) .not_to respond_to(:created_at) end it "allows overriding created_at for objects with created_at" do created_at = 3.days.ago stubbed = build_stubbed(:thing_with_timestamp, created_at: created_at) expect(stubbed.created_at).to be_within(1.second).of created_at end it "doesn't allow setting created_at on an object that doesn't define it" do expect { build_stubbed(:thing_without_timestamp, created_at: Time.now) } .to raise_error(NoMethodError, /created_at=/) end it "allows assignment of created_at" do stub = build_stubbed(:thing_with_timestamp) expect(stub.created_at).to be_about_now past_time = 3.days.ago stub.created_at = past_time expect(stub.created_at).to be_within(1.second).of past_time end it "behaves the same as a non-stubbed created_at" do define_model("ThingWithCreatedAt", created_at: :datetime) do def created_at :the_real_created_at end end FactoryBot.define do factory :thing_with_created_at end stub = build_stubbed(:thing_with_created_at) persisted = create(:thing_with_created_at) expect(stub.created_at).to eq(persisted.created_at) end end describe "defaulting `updated_at`" do include FactoryBot::Syntax::Methods before do define_model("ThingWithTimestamp", updated_at: :datetime) define_model("ThingWithoutTimestamp") FactoryBot.define do factory :thing_with_timestamp factory :thing_without_timestamp end end it "defaults updated_at for objects with updated_at" do expect(build_stubbed(:thing_with_timestamp).updated_at).to be_about_now end it "is doesn't mark the object as changed" do stub = build_stubbed(:thing_with_timestamp) expect(stub).not_to be_changed end it "doesn't add updated_at to objects who don't have the method" do expect(build_stubbed(:thing_without_timestamp)) .not_to respond_to(:updated_at) end it "allows overriding updated_at for objects with updated_at" do past_time = 3.days.ago stubbed = build_stubbed(:thing_with_timestamp, updated_at: past_time) expect(stubbed.updated_at).to be_within(1.second).of past_time end it "doesn't allow setting updated_at on an object that doesn't define it" do expect { build_stubbed(:thing_without_timestamp, updated_at: Time.now) }.to raise_error(NoMethodError, /updated_at=/) end it "allows assignment of updated_at" do stub = build_stubbed(:thing_with_timestamp) expect(stub.updated_at).to be_about_now past_time = 3.days.ago stub.updated_at = past_time expect(stub.updated_at).to be_within(1.second).of past_time end it "behaves the same as a non-stubbed updated_at" do define_model("ThingWithUpdatedAt", updated_at: :datetime) do def updated_at :the_real_updated_at end end FactoryBot.define do factory :thing_with_updated_at end stub = build_stubbed(:thing_with_updated_at) persisted = create(:thing_with_updated_at) expect(stub.updated_at).to eq(persisted.updated_at) end end describe "defaulting `id`" do before do define_model("Post") FactoryBot.define do factory :post end end it "allows overriding id" do expect(FactoryBot.build_stubbed(:post, id: 12).id).to eq 12 end end describe "configuring the starting id" do it "defines which id build_stubbed instances start with" do define_model("Post") FactoryBot.define do factory :post end FactoryBot.build_stubbed_starting_id = 1000 expect(FactoryBot.build_stubbed(:post).id).to eq 1000 FactoryBot.build_stubbed_starting_id = 3000 expect(FactoryBot.build_stubbed(:post).id).to eq 3000 end end factory_bot-6.5.6/spec/acceptance/callbacks_spec.rb000066400000000000000000000405361507676517300223720ustar00rootroot00000000000000describe "callbacks" do before do define_model("User", first_name: :string, last_name: :string) end context "with strategy callbacks" do before do FactoryBot.define do factory :user_with_callbacks, class: :user do after(:stub) { |user| user.first_name = "Stubby" } after(:build) { |user| user.first_name = "Buildy" } after(:create) { |user| user.last_name = "Createy" } end factory :user_with_inherited_callbacks, parent: :user_with_callbacks do after(:stub) { |user| user.last_name = "Double-Stubby" } after(:build) { |user| user.first_name = "Child-Buildy" } end factory :user_with_multi_called_callbacks, class: :user do first_name { "Jane" } trait(:alias_2) { alias_1 } trait(:alias_1) { surname } trait :surname do after(:build) { |user| user.first_name += " Doe" } end end end end it "runs the after(:stub) callback when stubbing" do user = FactoryBot.build_stubbed(:user_with_callbacks) expect(user.first_name).to eq "Stubby" end it "runs the after(:build) callback when building" do user = FactoryBot.build(:user_with_callbacks) expect(user.first_name).to eq "Buildy" end it "runs both the after(:build) and after(:create) callbacks when creating" do user = FactoryBot.create(:user_with_callbacks) expect(user.first_name).to eq "Buildy" expect(user.last_name).to eq "Createy" end it "runs both the after(:stub) callback on the factory and the inherited after(:stub) callback" do user = FactoryBot.build_stubbed(:user_with_inherited_callbacks) expect(user.first_name).to eq "Stubby" expect(user.last_name).to eq "Double-Stubby" end it "runs child callback after parent callback" do user = FactoryBot.build(:user_with_inherited_callbacks) expect(user.first_name).to eq "Child-Buildy" end it "only runs each callback once per instance" do user_1 = FactoryBot.build(:user_with_multi_called_callbacks, :surname, :alias_1, :alias_2) user_2 = FactoryBot.build(:user_with_multi_called_callbacks, :alias_1, :alias_2, :surname) user_3 = FactoryBot.build(:user_with_multi_called_callbacks, :alias_2, :surname, :alias_1) expect(user_1.first_name).to eq "Jane Doe" expect(user_2.first_name).to eq "Jane Doe" expect(user_3.first_name).to eq "Jane Doe" end end # with strategy callbacks context "with before(:all) and after(:all) included" do context "are executed in the correct order" do before do FactoryBot.define do before(:all) { TestLog << "global before-all called" } after(:all) { TestLog << "global after-all called" } before(:build) { TestLog << "global before-build called" } after(:build) { TestLog << "global after-build called" } before(:create) { TestLog << "global before-create called" } after(:create) { TestLog << "global after-create called" } factory :parent, class: :user do before(:all) { TestLog << "parent before-all called" } after(:all) { TestLog << "parent after-all called" } before(:build) { TestLog << "parent before-build called" } after(:build) { TestLog << "parent after-build called" } before(:create) { TestLog << "parent before-create called" } after(:create) { TestLog << "parent after-create called" } trait :parent_trait_1 do before(:all) { TestLog << "parent-trait-1 before-all called" } after(:all) { TestLog << "parent-trait-1 after-all called" } before(:build) { TestLog << "parent-trait-1 before-build called" } after(:build) { TestLog << "parent-trait-1 after-build called" } before(:create) { TestLog << "parent-trait-1 before-create called" } after(:create) { TestLog << "parent-trait-1 after-create called" } end trait :parent_trait_2 do before(:all) { TestLog << "parent-trait-2 before-all called" } after(:all) { TestLog << "parent-trait-2 after-all called" } before(:build) { TestLog << "parent-trait-2 before-build called" } after(:build) { TestLog << "parent-trait-2 after-build called" } before(:create) { TestLog << "parent-trait-2 before-create called" } after(:create) { TestLog << "parent-trait-2 after-create called" } end end factory :child, parent: :parent do before(:all) { TestLog << "child before-all called" } after(:create) { TestLog << "child after-create called" } after(:build) { TestLog << "child after-build called" } before(:build) { TestLog << "child before-build called" } before(:create) { TestLog << "child before-create called" } after(:all) { TestLog << "child after-all called" } trait :child_trait do before(:all) { TestLog << "child-trait before-all called" } after(:all) { TestLog << "child-trait after-all called" } before(:build) { TestLog << "child-trait before-build called" } after(:build) { TestLog << "child-trait after-build called" } before(:create) { TestLog << "child-trait before-create called" } after(:create) { TestLog << "child-trait after-create called" } end end end end before(:each) { TestLog.reset! } it "with trait callbacks executed in the order requested" do # Note: trait callbacks are executed AFTER any factory callbacks and # in the order they are requested. # FactoryBot.create(:child, :parent_trait_2, :child_trait, :parent_trait_1) expect(TestLog.size).to eq 36 # before(:all) expect(TestLog[0..5]).to eq [ "global before-all called", "parent before-all called", "child before-all called", "parent-trait-2 before-all called", "child-trait before-all called", "parent-trait-1 before-all called" ] # before(:build) expect(TestLog[6..11]).to eq [ "global before-build called", "parent before-build called", "child before-build called", "parent-trait-2 before-build called", "child-trait before-build called", "parent-trait-1 before-build called" ] # after(:build) expect(TestLog[12..17]).to eq [ "global after-build called", "parent after-build called", "child after-build called", "parent-trait-2 after-build called", "child-trait after-build called", "parent-trait-1 after-build called" ] # before(:create) expect(TestLog[18..23]).to eq [ "global before-create called", "parent before-create called", "child before-create called", "parent-trait-2 before-create called", "child-trait before-create called", "parent-trait-1 before-create called" ] # after(:create) expect(TestLog[24..29]).to eq [ "global after-create called", "parent after-create called", "child after-create called", "parent-trait-2 after-create called", "child-trait after-create called", "parent-trait-1 after-create called" ] # after(:all) expect(TestLog[30..35]).to eq [ "global after-all called", "parent after-all called", "child after-all called", "parent-trait-2 after-all called", "child-trait after-all called", "parent-trait-1 after-all called" ] end end # ordered correctly context "with context provided to before(:all)" do before(:each) { TestLog.reset! } it "receives 'nil' as the instance" do FactoryBot.define do before(:all) { |user| TestLog << "Global instance: #{user}" } factory :user do before(:all) { |user| TestLog << "Factory instance: #{user}" } end end FactoryBot.build(:user) expect(TestLog.first).to eq "Global instance: " expect(TestLog.last).to eq "Factory instance: " end it "receives the context, without an instance" do FactoryBot.define do before(:all) do |user, context| TestLog << "Global strategy: #{context.instance_values["build_strategy"].to_sym}" TestLog << "Global instance: #{context.instance}" end factory :user do before(:all) do |user, context| TestLog << "Factory strategy: #{context.instance_values["build_strategy"].to_sym}" TestLog << "Factory instance: #{context.instance}" end end end FactoryBot.build(:user) expect(TestLog.all).to eq [ "Global strategy: build", "Global instance: ", "Factory strategy: build", "Factory instance: " ] end end # context: with context provided to before(:all) context "with context provided to after(:all)" do it "succeeds with the instance provided" do FactoryBot.define do after(:all) { |user| user.first_name = "Globy" } factory :user do after(:all) { |user| user.last_name = "Lasty" } end end user = FactoryBot.build(:user) expect(user.first_name).to eq "Globy" expect(user.last_name).to eq "Lasty" end it "succeeds with both the instance and context provided" do FactoryBot.define do after(:all) { |user, context| user.first_name = context.new_first_name } factory :user do transient do new_first_name { "New First Name" } new_last_name { "New Last Name" } end after(:all) { |user, context| user.last_name = context.new_last_name } end end user = FactoryBot.build(:user) expect(user.first_name).to eq "New First Name" expect(user.last_name).to eq "New Last Name" end end # context: with context provided to after(:all) end # context: with before(:all) and after(:all) callbacks included end # describe: callbacks describe "callbacks using Symbol#to_proc" do before do define_model("User") do def confirmed? !!@confirmed end def confirm! @confirmed = true end end FactoryBot.define do factory :user do after :build, &:confirm! end end end it "runs the callback correctly" do user = FactoryBot.build(:user) expect(user).to be_confirmed end end describe "callbacks using syntax methods without referencing FactoryBot explicitly" do before do define_model("User", first_number: :integer, last_number: :integer) FactoryBot.define do sequence(:sequence_1) sequence(:sequence_2) sequence(:sequence_3) factory :user do after(:stub) { generate(:sequence_3) } after(:build) { |user| user.first_number = generate(:sequence_1) } after(:create) { |user, _evaluator| user.last_number = generate(:sequence_2) } end end end it "works when the callback has no variables" do FactoryBot.build_stubbed(:user) expect(FactoryBot.generate(:sequence_3)).to eq 2 end it "works when the callback has one variable" do expect(FactoryBot.build(:user).first_number).to eq 1 end it "works when the callback has two variables" do expect(FactoryBot.create(:user).last_number).to eq 1 end end describe "custom callbacks" do let(:custom_before) do Class.new do def result(evaluation) evaluation.object.tap do |instance| evaluation.notify(:before_custom, instance) end end end end let(:custom_after) do Class.new do def result(evaluation) evaluation.object.tap do |instance| evaluation.notify(:after_custom, instance) end end end end let(:totally_custom) do Class.new do def result(evaluation) evaluation.object.tap do |instance| evaluation.notify(:totally_custom, instance) end end end end before do define_model("User", first_name: :string, last_name: :string) do def name [first_name, last_name].join(" ") end end FactoryBot.register_strategy(:custom_before, custom_before) FactoryBot.register_strategy(:custom_after, custom_after) FactoryBot.register_strategy(:totally_custom, totally_custom) FactoryBot.define do factory :user do first_name { "John" } last_name { "Doe" } before(:custom) { |instance| instance.first_name = "Overridden First" } after(:custom) { |instance| instance.last_name = "Overridden Last" } callback(:totally_custom) do |instance| instance.first_name = "Totally" instance.last_name = "Custom" end end end end it "runs a custom before callback when the proper strategy executes" do expect(FactoryBot.build(:user).name).to eq "John Doe" expect(FactoryBot.custom_before(:user).name).to eq "Overridden First Doe" end it "runs a custom after callback when the proper strategy executes" do expect(FactoryBot.build(:user).name).to eq "John Doe" expect(FactoryBot.custom_after(:user).name).to eq "John Overridden Last" end it "runs a custom callback without prepending before or after when the proper strategy executes" do expect(FactoryBot.build(:user).name).to eq "John Doe" expect(FactoryBot.totally_custom(:user).name).to eq "Totally Custom" end end describe "binding a callback to multiple callbacks" do before do define_model("User", name: :string) FactoryBot.define do factory :user do callback(:before_create, :after_stub) do |instance| instance.name = instance.name.upcase end end end end it "binds the callback to creation" do expect(FactoryBot.create(:user, name: "John Doe").name).to eq "JOHN DOE" end it "does not bind the callback to building" do expect(FactoryBot.build(:user, name: "John Doe").name).to eq "John Doe" end it "binds the callback to stubbing" do expect(FactoryBot.build_stubbed(:user, name: "John Doe").name).to eq "JOHN DOE" end end describe "global callbacks" do include FactoryBot::Syntax::Methods before do define_model("User", name: :string) define_model("Company", name: :string) FactoryBot.define do after :build do |object| object.name = case object.class.to_s when "User" then "John Doe" when "Company" then "Acme Suppliers" end end after :create do |object| object.name = "#{object.name}!!!" end trait :awesome do after :build do |object| object.name = "___#{object.name}___" end after :create do |object| object.name = "A#{object.name}Z" end end factory :user do after :build do |user| user.name = user.name.downcase end end factory :company do after :build do |company| company.name = company.name.upcase end end end end it "triggers after build callbacks for all factories" do expect(build(:user).name).to eq "john doe" expect(create(:user).name).to eq "john doe!!!" expect(create(:user, :awesome).name).to eq "A___john doe___!!!Z" expect(build(:company).name).to eq "ACME SUPPLIERS" end end describe "before build callback" do before do define_class("TitleSetter") do def self.title=(new_title) class_variable_set(:@@title, new_title) end def self.title class_variable_get(:@@title) end end define_model("Article", title: :string) FactoryBot.define do factory :article_with_before_callbacks, class: :article do before(:build) { TitleSetter.title = "title from before build" } after(:build) { TitleSetter.title = "title from after build" } title { TitleSetter.title } end end end it "runs the before callback" do article = FactoryBot.build(:article_with_before_callbacks) expect(article.title).to eq("title from before build") end end factory_bot-6.5.6/spec/acceptance/create_list_spec.rb000066400000000000000000000055641507676517300227530ustar00rootroot00000000000000describe "create multiple instances" do before do define_model("Post", title: :string, position: :integer) FactoryBot.define do factory(:post) do |post| post.title { "Through the Looking Glass" } post.position { rand(10**4) } end end end context "without default attributes" do subject { FactoryBot.create_list(:post, 20) } its(:length) { should eq 20 } it "creates all the posts" do subject.each do |record| expect(record).not_to be_new_record end end it "uses the default factory values" do subject.each do |record| expect(record.title).to eq "Through the Looking Glass" end end end context "with default attributes" do subject { FactoryBot.create_list(:post, 20, title: "The Hunting of the Snark") } it "overrides the default values" do subject.each do |record| expect(record.title).to eq "The Hunting of the Snark" end end end context "with a block" do subject do FactoryBot.create_list(:post, 20, title: "The Listing of the Block") do |post| post.position = post.id end end it "uses the new values" do subject.each do |record| expect(record.position).to eq record.id end end end context "with a block that receives both the object and an index" do subject do FactoryBot.create_list(:post, 20, title: "The Indexed Block") do |post, index| post.position = index end end it "uses the new values" do subject.each_with_index do |record, index| expect(record.position).to eq index end end end context "without the count" do subject { FactoryBot.create_list(:post, title: "The Hunting of the Bear") } it "raise ArgumentError with the proper error message" do expect { subject }.to raise_error(ArgumentError, /count missing/) end end end describe "multiple creates and transient attributes to dynamically build attribute lists" do before do define_model("User", name: :string) do has_many :posts end define_model("Post", title: :string, user_id: :integer) do belongs_to :user end FactoryBot.define do factory :post do title { "Through the Looking Glass" } user end factory :user do name { "John Doe" } factory :user_with_posts do transient do posts_count { 5 } end after(:create) do |user, evaluator| FactoryBot.create_list(:post, evaluator.posts_count, user: user) end end end end end it "generates the correct number of posts" do expect(FactoryBot.create(:user_with_posts).posts.length).to eq 5 end it "allows the number of posts to be modified" do expect(FactoryBot.create(:user_with_posts, posts_count: 2).posts.length).to eq 2 end end factory_bot-6.5.6/spec/acceptance/create_pair_spec.rb000066400000000000000000000012751507676517300227260ustar00rootroot00000000000000describe "create multiple instances" do before do define_model("Post", title: :string, position: :integer) FactoryBot.define do factory(:post) do |post| post.title { "Through the Looking Glass" } post.position { rand(10**4) } end end end context "without default attributes" do subject { FactoryBot.create_pair(:post) } its(:length) { should eq 2 } it "creates all the posts" do subject.each do |record| expect(record).not_to be_new_record end end it "uses the default factory values" do subject.each do |record| expect(record.title).to eq "Through the Looking Glass" end end end end factory_bot-6.5.6/spec/acceptance/create_spec.rb000066400000000000000000000050511507676517300217070ustar00rootroot00000000000000describe "a created instance" do include FactoryBot::Syntax::Methods before do define_model("User") define_model("Post", user_id: :integer) do belongs_to :user end FactoryBot.define do factory :user factory :post do user end end end subject { create("post") } it { should_not be_new_record } it "assigns and saves associations" do expect(subject.user).to be_kind_of(User) expect(subject.user).not_to be_new_record end end describe "a created instance, specifying strategy: :build" do include FactoryBot::Syntax::Methods before do define_model("User") define_model("Post", user_id: :integer) do belongs_to :user end FactoryBot.define do factory :user factory :post do association(:user, strategy: :build) end end end subject { create(:post) } it "saves associations (strategy: :build only affects build, not create)" do expect(subject.user).to be_kind_of(User) expect(subject.user).not_to be_new_record end end describe "a custom create" do include FactoryBot::Syntax::Methods before do define_class("User") do def initialize @persisted = false end def persist @persisted = true end def persisted? @persisted end end FactoryBot.define do factory :user do to_create(&:persist) end end end it "uses the custom create block instead of save" do expect(FactoryBot.create(:user)).to be_persisted end end describe "a custom create passing in an evaluator" do before do define_class("User") do attr_accessor :name end FactoryBot.define do factory :user do transient { creation_name { "evaluator" } } to_create do |user, evaluator| user.name = evaluator.creation_name end end end end it "passes the evaluator to the custom create block" do expect(FactoryBot.create(:user).name).to eq "evaluator" end end describe "calling `create` with a block" do include FactoryBot::Syntax::Methods before do define_model("Company", name: :string) FactoryBot.define do factory :company end end it "passes the created instance" do create(:company, name: "thoughtbot") do |company| expect(company.name).to eq("thoughtbot") end end it "returns the created instance" do expected = nil result = create(:company) { |company| expected = company "hello!" } expect(result).to eq expected end end factory_bot-6.5.6/spec/acceptance/define_child_before_parent_spec.rb000066400000000000000000000007101507676517300257310ustar00rootroot00000000000000describe "defining a child factory before a parent" do before do define_model("User", name: :string, admin: :boolean, email: :string, upper_email: :string, login: :string) FactoryBot.define do factory :admin, parent: :user do admin { true } end factory :user do name { "awesome" } end end end it "creates admin factories correctly" do expect(FactoryBot.create(:admin)).to be_admin end end factory_bot-6.5.6/spec/acceptance/defining_methods_inside_a_factory_spec.rb000066400000000000000000000015001507676517300273270ustar00rootroot00000000000000describe "defining methods inside FactoryBot" do it "raises with a meaningful message" do define_model("User") bad_factory_definition = -> do FactoryBot.define do factory :user do def generate_name "John Doe" end end end end expect(&bad_factory_definition).to raise_error( FactoryBot::MethodDefinitionError, /Defining methods in blocks \(trait or factory\) is not supported \(generate_name\)/ ) end it "accepts a method named :definition when set through :method_missing" do define_model("User", definition: :string) FactoryBot.define do factory :user do definition do "Jester" end end end user = FactoryBot.build(:user) expect(user.definition).to eq("Jester") end end factory_bot-6.5.6/spec/acceptance/definition_camel_string_spec.rb000066400000000000000000000005411507676517300253220ustar00rootroot00000000000000describe "an instance generated by a factory named a camel case string " do before do define_model("UserModel") FactoryBot.define do factory "UserModel", class: UserModel end end it "registers the UserModel factory" do expect(FactoryBot::Internal.factory_by_name("UserModel")) .to be_a(FactoryBot::Factory) end end factory_bot-6.5.6/spec/acceptance/definition_spec.rb000066400000000000000000000023071507676517300225750ustar00rootroot00000000000000describe "an instance generated by a factory with a custom class name" do before do define_model("User", admin: :boolean) FactoryBot.define do factory :user factory :admin, class: User do admin { true } end end end subject { FactoryBot.create(:admin) } it { should be_kind_of(User) } it { should be_admin } end describe "attributes defined using Symbol#to_proc" do before do define_model("User", password: :string, password_confirmation: :string) FactoryBot.define do factory :user do password { "foo" } password_confirmation(&:password) end end end it "assigns values correctly" do user = FactoryBot.build(:user) expect(user.password).to eq "foo" expect(user.password_confirmation).to eq "foo" end it "assigns value with override correctly" do user = FactoryBot.build(:user, password: "bar") expect(user.password).to eq "bar" expect(user.password_confirmation).to eq "bar" end it "assigns overridden value correctly" do user = FactoryBot.build(:user, password_confirmation: "bar") expect(user.password).to eq "foo" expect(user.password_confirmation).to eq "bar" end end factory_bot-6.5.6/spec/acceptance/definition_without_block_spec.rb000066400000000000000000000004361507676517300255330ustar00rootroot00000000000000describe "an instance generated by a factory" do before do define_model("User") FactoryBot.define do factory :user end end it "registers the user factory" do expect(FactoryBot::Internal.factory_by_name(:user)) .to be_a(FactoryBot::Factory) end end factory_bot-6.5.6/spec/acceptance/enum_traits_spec.rb000066400000000000000000000101741507676517300230000ustar00rootroot00000000000000describe "enum traits" do def define_model_with_enum(class_name, field, values) define_model(class_name, status: :integer) do if ActiveRecord::VERSION::STRING >= "7.0" enum field, values else enum field => values end end end context "when automatically_define_enum_traits is true" do it "builds traits automatically for model enum field" do define_model_with_enum("Task", :status, {queued: 0, started: 1, finished: 2}) FactoryBot.define do factory :task end Task.statuses.each_key do |trait_name| task = FactoryBot.build(:task, trait_name) expect(task.status).to eq(trait_name) end Task.reset_column_information end it "prefers user defined traits over automatically built traits" do define_model_with_enum("Task", :status, {queued: 0, started: 1, finished: 2}) FactoryBot.define do factory :task do trait :queued do status { :finished } end trait :started do status { :finished } end trait :finished do status { :finished } end end end Task.statuses.each_key do |trait_name| task = FactoryBot.build(:task, trait_name) expect(task.status).to eq("finished") end Task.reset_column_information end it "builds traits for each enumerated value using a provided list of values as a Hash" do statuses = {queued: 0, started: 1, finished: 2} define_class "Task" do attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum :status, statuses end end statuses.each do |trait_name, trait_value| task = FactoryBot.build(:task, trait_name) expect(task.status).to eq(trait_value) end end it "builds traits for each enumerated value using a provided list of values as an Array" do statuses = %w[queued started finished] define_class "Task" do attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum :status, statuses end end statuses.each do |trait_name| task = FactoryBot.build(:task, trait_name) expect(task.status).to eq(trait_name) end end it "builds traits for each enumerated value using a custom enumerable" do statuses = define_class("Statuses") { include Enumerable def each(&block) ["queued", "started", "finished"].each(&block) end }.new define_class "Task" do attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum :status, statuses end end statuses.each do |trait_name| task = FactoryBot.build(:task, trait_name) expect(task.status).to eq(trait_name) end end end context "when automatically_define_enum_traits is false" do it "raises an error for undefined traits" do with_temporary_assignment(FactoryBot, :automatically_define_enum_traits, false) do define_model_with_enum("Task", :status, {queued: 0, started: 1, finished: 2}) FactoryBot.define do factory :task end Task.statuses.each_key do |trait_name| expect { FactoryBot.build(:task, trait_name) }.to raise_error( KeyError, "Trait not registered: \"#{trait_name}\"" ) end Task.reset_column_information end end it "builds traits for each enumerated value when traits_for_enum are specified" do with_temporary_assignment(FactoryBot, :automatically_define_enum_traits, false) do define_model_with_enum("Task", :status, {queued: 0, started: 1, finished: 2}) FactoryBot.define do factory :task do traits_for_enum(:status) end end Task.statuses.each_key do |trait_name| task = FactoryBot.build(:task, trait_name) expect(task.status).to eq(trait_name) end Task.reset_column_information end end end end factory_bot-6.5.6/spec/acceptance/global_initialize_with_spec.rb000066400000000000000000000037771507676517300251750ustar00rootroot00000000000000describe "global initialize_with" do before do define_class("User") do attr_accessor :name def initialize(name) @name = name end end define_class("Post") do attr_reader :name def initialize(name) @name = name end end FactoryBot.define do initialize_with { new("initialize_with") } trait :with_initialize_with do initialize_with { new("trait initialize_with") } end factory :user do factory :child_user factory :child_user_with_trait do with_initialize_with end end factory :post do factory :child_post factory :child_post_with_trait do with_initialize_with end end end end it "handles base initialize_with" do expect(FactoryBot.build(:user).name).to eq "initialize_with" expect(FactoryBot.build(:post).name).to eq "initialize_with" end it "handles child initialize_with" do expect(FactoryBot.build(:child_user).name).to eq "initialize_with" expect(FactoryBot.build(:child_post).name).to eq "initialize_with" end it "handles child initialize_with with trait" do expect(FactoryBot.build(:child_user_with_trait).name).to eq "trait initialize_with" expect(FactoryBot.build(:child_post_with_trait).name).to eq "trait initialize_with" end it "handles inline trait override" do expect(FactoryBot.build(:child_user, :with_initialize_with).name).to eq "trait initialize_with" expect(FactoryBot.build(:child_post, :with_initialize_with).name).to eq "trait initialize_with" end it "uses initialize_with globally across FactoryBot.define" do define_class("Company") do attr_reader :name def initialize(name) @name = name end end FactoryBot.define do factory :company end expect(FactoryBot.build(:company).name).to eq "initialize_with" expect(FactoryBot.build(:company, :with_initialize_with).name).to eq "trait initialize_with" end end factory_bot-6.5.6/spec/acceptance/global_to_create_spec.rb000066400000000000000000000062501507676517300237330ustar00rootroot00000000000000describe "global to_create" do before do define_model("User", name: :string) define_model("Post", name: :string) FactoryBot.define do to_create { |instance| instance.name = "persisted!" } trait :override_to_create do to_create { |instance| instance.name = "override" } end factory :user do name { "John Doe" } factory :child_user factory :child_user_with_trait do override_to_create end end factory :post do name { "Great title" } factory :child_post factory :child_post_with_trait do override_to_create end end end end it "handles base to_create" do expect(FactoryBot.create(:user).name).to eq "persisted!" expect(FactoryBot.create(:post).name).to eq "persisted!" end it "handles child to_create" do expect(FactoryBot.create(:child_user).name).to eq "persisted!" expect(FactoryBot.create(:child_post).name).to eq "persisted!" end it "handles child to_create with trait" do expect(FactoryBot.create(:child_user_with_trait).name).to eq "override" expect(FactoryBot.create(:child_post_with_trait).name).to eq "override" end it "handles inline trait override" do user = FactoryBot.create(:child_user, :override_to_create) post = FactoryBot.create(:child_post, :override_to_create) expect(user.name).to eq "override" expect(post.name).to eq "override" end it "uses to_create globally across FactoryBot.define" do define_model("Company", name: :string) FactoryBot.define do factory :company end company = FactoryBot.create(:company) override_company = FactoryBot.create(:company, :override_to_create) expect(company.name).to eq "persisted!" expect(override_company.name).to eq "override" end end describe "global skip_create" do before do define_model("User", name: :string) define_model("Post", name: :string) FactoryBot.define do skip_create trait :override_to_create do to_create { |instance| instance.name = "override" } end factory :user do name { "John Doe" } factory :child_user factory :child_user_with_trait do override_to_create end end factory :post do name { "Great title" } factory :child_post factory :child_post_with_trait do override_to_create end end end end it "does not persist any record" do expect(FactoryBot.create(:user)).to be_new_record expect(FactoryBot.create(:post)).to be_new_record end it "does not persist child records" do expect(FactoryBot.create(:child_user)).to be_new_record expect(FactoryBot.create(:child_post)).to be_new_record end it "honors overridden to_create" do expect(FactoryBot.create(:child_user_with_trait).name).to eq "override" expect(FactoryBot.create(:child_post_with_trait).name).to eq "override" end it "honors inline trait to_create" do expect(FactoryBot.create(:child_user, :override_to_create).name).to eq "override" expect(FactoryBot.create(:child_post, :override_to_create).name).to eq "override" end end factory_bot-6.5.6/spec/acceptance/initialize_with_spec.rb000066400000000000000000000137261507676517300236500ustar00rootroot00000000000000describe "initialize_with with non-FG attributes" do include FactoryBot::Syntax::Methods before do define_model("User", name: :string, age: :integer) do def self.construct(name, age) new(name: name, age: age) end end FactoryBot.define do factory :user do initialize_with { User.construct("John Doe", 21) } end end end subject { build(:user) } its(:name) { should eq "John Doe" } its(:age) { should eq 21 } end describe "initialize_with with FG attributes that are transient" do include FactoryBot::Syntax::Methods before do define_model("User", name: :string) do def self.construct(name) new(name: "#{name} from .construct") end end FactoryBot.define do factory :user do transient do name { "Handsome Chap" } end initialize_with { User.construct(name) } end end end subject { build(:user) } its(:name) { should eq "Handsome Chap from .construct" } end describe "initialize_with non-ORM-backed objects" do include FactoryBot::Syntax::Methods before do define_class("ReportGenerator") do attr_reader :name, :data def initialize(name, data) @name = name @data = data end end FactoryBot.define do sequence(:random_data) { Array.new(5) { Kernel.rand(200) } } factory :report_generator do transient do name { "My Awesome Report" } end initialize_with { ReportGenerator.new(name, FactoryBot.generate(:random_data)) } end end end it "allows for overrides" do expect(build(:report_generator, name: "Overridden").name).to eq "Overridden" end it "generates random data" do expect(build(:report_generator).data.length).to eq 5 end end describe "initialize_with parent and child factories" do before do define_class("Awesome") do attr_reader :name def initialize(name) @name = name end end FactoryBot.define do factory :awesome do transient do name { "Great" } end initialize_with { Awesome.new(name) } factory :sub_awesome do transient do name { "Sub" } end end factory :super_awesome do initialize_with { Awesome.new("Super") } end end end end it "uses the parent's constructor when the child factory doesn't assign it" do expect(FactoryBot.build(:sub_awesome).name).to eq "Sub" end it "allows child factories to override initialize_with" do expect(FactoryBot.build(:super_awesome).name).to eq "Super" end end describe "initialize_with implicit constructor" do before do define_class("Awesome") do attr_reader :name def initialize(name) @name = name end end FactoryBot.define do factory :awesome do transient do name { "Great" } end initialize_with { new(name) } end end end it "instantiates the correct object" do expect(FactoryBot.build(:awesome, name: "Awesome name").name).to eq "Awesome name" end end describe "initialize_with doesn't duplicate assignment on attributes accessed from initialize_with" do before do define_class("User") do attr_reader :name attr_accessor :email def initialize(name) @name = name end end FactoryBot.define do sequence(:email) { |n| "person#{n}@example.com" } factory :user do email name { email.gsub(/@.+/, "") } initialize_with { new(name) } end end end it "instantiates the correct object" do built_user = FactoryBot.build(:user) expect(built_user.name).to eq "person1" expect(built_user.email).to eq "person1@example.com" end end describe "initialize_with has access to all attributes for construction" do it "assigns attributes correctly" do define_class("User") do attr_reader :name, :email, :ignored def initialize(attributes = {}) @name = attributes[:name] @email = attributes[:email] @ignored = attributes[:ignored] end end FactoryBot.define do sequence(:email) { |n| "person#{n}@example.com" } factory :user do transient do ignored { "of course!" } end email name { email.gsub(/@.+/, "") } initialize_with { new(**attributes) } end end user_with_attributes = FactoryBot.build(:user) expect(user_with_attributes.email).to eq "person1@example.com" expect(user_with_attributes.name).to eq "person1" expect(user_with_attributes.ignored).to be_nil end end describe "initialize_with with an 'attributes' attribute" do it "assigns attributes correctly" do define_class("User") do attr_reader :name def initialize(attributes:) @name = attributes[:name] end end FactoryBot.define do factory :user do attributes { {name: "Daniel"} } initialize_with { new(**attributes) } end end user = FactoryBot.build(:user) expect(user.name).to eq("Daniel") end end describe "initialize_with for a constructor that requires a block" do it "executes the block correctly" do define_class("Awesome") do attr_reader :output def initialize(&block) @output = instance_exec(&block) end end FactoryBot.define do factory :awesome do initialize_with { new { "Output" } } end end expect(FactoryBot.build(:awesome).output).to eq "Output" end end describe "initialize_with with a hash argument" do it "builds the object correctly" do define_class("Container") do attr_reader :contents def initialize(contents) @contents = contents end end FactoryBot.define do factory :container do initialize_with { new({key: :value}) } end end expect(FactoryBot.build(:container).contents).to eq({key: :value}) end end factory_bot-6.5.6/spec/acceptance/keyed_by_class_spec.rb000066400000000000000000000005521507676517300234250ustar00rootroot00000000000000describe "finding factories keyed by class instead of symbol" do before do define_model("User") do attr_accessor :name, :email end FactoryBot.define do factory :user end end it "doesn't find the factory" do expect { FactoryBot.create(User) }.to( raise_error(KeyError, /Factory not registered: User/) ) end end factory_bot-6.5.6/spec/acceptance/lint_spec.rb000066400000000000000000000116201507676517300214110ustar00rootroot00000000000000describe "FactoryBot.lint" do it "raises when a factory is invalid" do define_model "User", name: :string do validates :name, presence: true end define_model "AlwaysValid" FactoryBot.define do factory :user do factory :admin_user end factory :always_valid end error_message = <<~ERROR_MESSAGE.strip The following factories are invalid: * user - Validation failed: Name can't be blank (ActiveRecord::RecordInvalid) * admin_user - Validation failed: Name can't be blank (ActiveRecord::RecordInvalid) ERROR_MESSAGE expect { FactoryBot.lint }.to raise_error FactoryBot::InvalidFactoryError, error_message end it "executes linting in an ActiveRecord::Base transaction" do define_model "User", name: :string do validates :name, uniqueness: true end define_model "AlwaysValid" FactoryBot.define do factory :user do factory :admin_user end factory :always_valid end expect { FactoryBot.lint }.to_not raise_error end it "does not raise when all factories are valid" do define_model "User", name: :string do validates :name, presence: true end FactoryBot.define do factory :user do name { "assigned" } end end expect { FactoryBot.lint }.not_to raise_error end it "allows for selective linting" do define_model "InvalidThing", name: :string do validates :name, presence: true end define_model "ValidThing", name: :string FactoryBot.define do factory :valid_thing factory :invalid_thing end expect { only_valid_factories = FactoryBot.factories.reject { |factory| factory.name =~ /invalid/ } FactoryBot.lint only_valid_factories }.not_to raise_error end describe "trait validation" do context "enabled" do it "raises if a trait produces an invalid object" do define_model "User", name: :string do validates :name, presence: true end FactoryBot.define do factory :user do name { "Yep" } trait :unnamed do name { nil } end end end error_message = <<~ERROR_MESSAGE.strip The following factories are invalid: * user+unnamed - Validation failed: Name can't be blank (ActiveRecord::RecordInvalid) ERROR_MESSAGE expect { FactoryBot.lint traits: true }.to raise_error FactoryBot::InvalidFactoryError, error_message end it "does not raise if a trait produces a valid object" do define_model "User", name: :string do validates :name, presence: true end FactoryBot.define do factory :user do name { "Yep" } trait :renamed do name { "Yessir" } end end end expect { FactoryBot.lint traits: true }.not_to raise_error end end context "disabled" do it "does not raises if a trait produces an invalid object" do define_model "User", name: :string do validates :name, presence: true end FactoryBot.define do factory :user do name { "Yep" } trait :unnamed do name { nil } end end end expect { FactoryBot.lint traits: false FactoryBot.lint }.not_to raise_error end end end describe "factory strategy for linting" do it "uses the requested strategy" do define_class "User" do attr_accessor :name def save! raise "expected :build strategy, #save! shouldn't be invoked" end end FactoryBot.define do factory :user do name { "Barbara" } end end expect { FactoryBot.lint strategy: :build }.not_to raise_error end it "uses the requested strategy during trait validation" do define_class "User" do attr_accessor :name def save! raise "expected :build strategy, #save! shouldn't be invoked" end end FactoryBot.define do factory :user do name { "Barbara" } trait :male do name { "Bob" } end end end expect { FactoryBot.lint traits: true, strategy: :build }.not_to raise_error end end describe "verbose linting" do it "prints the backtrace for each factory error" do define_class("InvalidThing") do def save! raise "invalid" end end FactoryBot.define do factory :invalid_thing end expect { FactoryBot.lint(verbose: true) }.to raise_error( FactoryBot::InvalidFactoryError, %r{#{__FILE__}:\d*:in ('InvalidThing#save!'|`save!')} ) end end end factory_bot-6.5.6/spec/acceptance/modify_factories_spec.rb000066400000000000000000000105641507676517300237770ustar00rootroot00000000000000describe "modifying factories" do include FactoryBot::Syntax::Methods before do define_model("User", name: :string, admin: :boolean, email: :string, login: :string) FactoryBot.define do sequence(:email) { |n| "user#{n}@example.com" } factory :user do email after(:create) do |user| user.login = user.name.upcase if user.name end factory :admin do admin { true } end end end end context "simple modification" do before do FactoryBot.modify do factory :user do name { "Great User" } end end end subject { create(:user) } its(:name) { should eq "Great User" } its(:login) { should eq "GREAT USER" } it "doesn't allow the factory to be subsequently defined" do expect { FactoryBot.define { factory :user } }.to raise_error(FactoryBot::DuplicateDefinitionError, "Factory already registered: user") end it "does allow the factory to be subsequently modified" do FactoryBot.modify do factory :user do name { "Overridden again!" } end end expect(create(:user).name).to eq "Overridden again!" end end context "adding callbacks" do before do FactoryBot.modify do factory :user do name { "Great User" } after(:create) do |user| user.name = user.name.downcase user.login = nil end end end end subject { create(:user) } its(:name) { should eq "great user" } its(:login) { should be_nil } end context "reusing traits" do before do FactoryBot.define do trait :rockstar do name { "Johnny Rockstar!!!" } end end FactoryBot.modify do factory :user do rockstar email { "#{name}@example.com" } end end end subject { create(:user) } its(:name) { should eq "Johnny Rockstar!!!" } its(:email) { should eq "Johnny Rockstar!!!@example.com" } its(:login) { should eq "JOHNNY ROCKSTAR!!!" } end context "redefining attributes" do before do FactoryBot.modify do factory :user do email { "#{name}-modified@example.com" } name { "Great User" } end end end context "creating user" do context "without overrides" do subject { create(:user) } its(:name) { should eq "Great User" } its(:email) { should eq "Great User-modified@example.com" } end context "overriding the email" do subject { create(:user, email: "perfect@example.com") } its(:name) { should eq "Great User" } its(:email) { should eq "perfect@example.com" } end context "overriding the name" do subject { create(:user, name: "wonderful") } its(:name) { should eq "wonderful" } its(:email) { should eq "wonderful-modified@example.com" } end end context "creating admin" do context "without overrides" do subject { create(:admin) } its(:name) { should eq "Great User" } its(:email) { should eq "Great User-modified@example.com" } its(:admin) { should be true } end context "overriding the email" do subject { create(:admin, email: "perfect@example.com") } its(:name) { should eq "Great User" } its(:email) { should eq "perfect@example.com" } its(:admin) { should be true } end context "overriding the name" do subject { create(:admin, name: "wonderful") } its(:name) { should eq "wonderful" } its(:email) { should eq "wonderful-modified@example.com" } its(:admin) { should be true } end end end it "doesn't overwrite already defined child's attributes" do FactoryBot.modify do factory :user do admin { false } end end expect(create(:admin)).to be_admin end it "allows for overriding child classes" do FactoryBot.modify do factory :admin do admin { false } end end expect(create(:admin)).not_to be_admin end it "raises an exception if the factory was not defined before" do modify_unknown_factory = -> do FactoryBot.modify do factory :unknown_factory end end expect(&modify_unknown_factory).to raise_error(KeyError) end end factory_bot-6.5.6/spec/acceptance/modify_inherited_spec.rb000066400000000000000000000025301507676517300237650ustar00rootroot00000000000000describe "modifying inherited factories with traits" do before do define_model("User", gender: :string, admin: :boolean, age: :integer) FactoryBot.define do factory :user do trait(:female) { gender { "Female" } } trait(:male) { gender { "Male" } } trait(:young_admin) do admin { true } age { 17 } end female young_admin factory :female_user do gender { "Female" } age { 25 } end factory :male_user do gender { "Male" } end end end end it "returns the correct value for overridden attributes from traits" do expect(FactoryBot.build(:male_user).gender).to eq "Male" end it "returns the correct value for overridden attributes from traits defining multiple attributes" do expect(FactoryBot.build(:female_user).gender).to eq "Female" expect(FactoryBot.build(:female_user).age).to eq 25 expect(FactoryBot.build(:female_user).admin).to eq true end it "allows modification of attributes created via traits" do FactoryBot.modify do factory :male_user do age { 20 } end end expect(FactoryBot.build(:male_user).gender).to eq "Male" expect(FactoryBot.build(:male_user).age).to eq 20 expect(FactoryBot.build(:male_user).admin).to eq true end end factory_bot-6.5.6/spec/acceptance/nested_attributes_spec.rb000066400000000000000000000015601507676517300241750ustar00rootroot00000000000000describe "association assignment from nested attributes" do before do define_model("Post", title: :string) do has_many :comments accepts_nested_attributes_for :comments end define_model("Comment", post_id: :integer, body: :text) do belongs_to :post end FactoryBot.define do factory :post do comments_attributes { [FactoryBot.attributes_for(:comment), FactoryBot.attributes_for(:comment)] } end factory :comment do sequence(:body) { |n| "Body #{n}" } end end end it "assigns the correct amount of comments" do expect(FactoryBot.create(:post).comments.count).to eq 2 end it "assigns the correct amount of comments when overridden" do post = FactoryBot.create(:post, comments_attributes: [FactoryBot.attributes_for(:comment)]) expect(post.comments.count).to eq 1 end end factory_bot-6.5.6/spec/acceptance/overrides_spec.rb000066400000000000000000000024101507676517300224420ustar00rootroot00000000000000describe "attribute overrides" do before do define_model("User", admin: :boolean) define_model("Post", title: :string, secure: :boolean, user_id: :integer) do belongs_to :user def secure=(value) return unless user&.admin? write_attribute(:secure, value) end end FactoryBot.define do factory :user do factory :admin do admin { true } end end factory :post do user title { "default title" } end end end let(:admin) { FactoryBot.create(:admin) } let(:post_attributes) do {secure: false} end let(:non_admin_post_attributes) do post_attributes[:user] = FactoryBot.create(:user) post_attributes end let(:admin_post_attributes) do post_attributes[:user] = admin post_attributes end context "with an admin posting" do subject { FactoryBot.create(:post, admin_post_attributes) } its(:secure) { should eq false } end context "with a non-admin posting" do subject { FactoryBot.create(:post, non_admin_post_attributes) } its(:secure) { should be_nil } end context "with no user posting" do subject { FactoryBot.create(:post, post_attributes) } its(:secure) { should be_nil } end end factory_bot-6.5.6/spec/acceptance/parent_spec.rb000066400000000000000000000032241507676517300217350ustar00rootroot00000000000000describe "an instance generated by a factory that inherits from another factory" do before do define_model("User", name: :string, admin: :boolean, email: :string, upper_email: :string, login: :string) FactoryBot.define do factory :user do name { "John" } email { "#{name.downcase}@example.com" } login { email } factory :admin do name { "admin" } admin { true } upper_email { email.upcase } end end end end describe "the parent class" do subject { FactoryBot.create(:user) } it { should_not be_admin } its(:name) { should eq "John" } its(:email) { should eq "john@example.com" } its(:login) { should eq "john@example.com" } end describe "the child class redefining parent's attributes" do subject { FactoryBot.create(:admin) } it { should be_kind_of(User) } it { should be_admin } its(:name) { should eq "admin" } its(:email) { should eq "admin@example.com" } its(:login) { should eq "admin@example.com" } its(:upper_email) { should eq "ADMIN@EXAMPLE.COM" } end end describe "nested factories with different parents" do before do define_model("User", name: :string) FactoryBot.define do factory :user do name { "Basic User" } factory :male_user do name { "John Doe" } end factory :uppercase_male_user, parent: :male_user do after(:build) { |user| user.name = user.name.upcase } end end end end it "honors :parent over the factory block nesting" do expect(FactoryBot.build(:uppercase_male_user).name).to eq "JOHN DOE" end end factory_bot-6.5.6/spec/acceptance/private_attributes_spec.rb000066400000000000000000000005161507676517300243650ustar00rootroot00000000000000describe "setting private attributes" do it "raises a NoMethodError" do define_class("User") do private attr_accessor :foo end FactoryBot.define do factory :user do foo { 123 } end end expect { FactoryBot.build(:user) }.to raise_error NoMethodError, /foo=/ end end factory_bot-6.5.6/spec/acceptance/register_strategies_spec.rb000066400000000000000000000076431507676517300245330ustar00rootroot00000000000000shared_context "registering custom strategies" do before do define_class("NamedObject") do attr_accessor :name end end let(:custom_strategy) do Class.new do def result(evaluation) evaluation.object.tap do |instance| instance.name = "Custom strategy" end end end end end describe "register custom strategies" do include_context "registering custom strategies" before do FactoryBot.define do factory :named_object do name { "Great" } end end end it "allows overriding default strategies" do expect(FactoryBot.build(:named_object).name).to eq "Great" FactoryBot.register_strategy(:build, custom_strategy) expect(FactoryBot.build(:named_object).name).to eq "Custom strategy" end it "allows adding additional strategies" do FactoryBot.register_strategy(:insert, custom_strategy) expect(FactoryBot.build(:named_object).name).to eq "Great" expect(FactoryBot.insert(:named_object).name).to eq "Custom strategy" end it "allows using the *_list method to build a list using a custom strategy" do FactoryBot.register_strategy(:insert, custom_strategy) inserted_items = FactoryBot.insert_list(:named_object, 2) expect(inserted_items.length).to eq 2 expect(inserted_items.map(&:name)).to eq ["Custom strategy", "Custom strategy"] end it "allows using the *_pair method to build a list using a custom strategy" do FactoryBot.register_strategy(:insert, custom_strategy) inserted_items = FactoryBot.insert_pair(:named_object) expect(inserted_items.length).to eq 2 expect(inserted_items.map(&:name)).to eq ["Custom strategy", "Custom strategy"] end end describe "including FactoryBot::Syntax::Methods when custom strategies have been declared" do include FactoryBot::Syntax::Methods include_context "registering custom strategies" before do FactoryBot.define do factory :named_object do name { "Great" } end end end it "allows adding additional strategies" do FactoryBot.register_strategy(:insert, custom_strategy) expect(insert(:named_object).name).to eq "Custom strategy" end end describe "associations without overriding :strategy" do include_context "registering custom strategies" before do define_model("Post", user_id: :integer) do belongs_to :user end define_model("User", name: :string) FactoryBot.define do factory :post do user end factory :user do name { "John Doe" } end end end context "when the :use_parent_strategy config option is set to false" do it "uses the overridden strategy on the association" do FactoryBot.register_strategy(:create, custom_strategy) with_temporary_assignment(FactoryBot, :use_parent_strategy, false) do post = FactoryBot.build(:post) expect(post.user.name).to eq "Custom strategy" end end end context "when the :use_parent_strategy config option is set to true" do it "uses the parent strategy on the association" do FactoryBot.register_strategy(:create, custom_strategy) with_temporary_assignment(FactoryBot, :use_parent_strategy, true) do post = FactoryBot.build(:post) expect(post.user.name).to eq "John Doe" end end end end describe "associations overriding :strategy" do include_context "registering custom strategies" before do define_model("Post", user_id: :integer) do belongs_to :user end define_model("User", name: :string) FactoryBot.define do factory :post do association :user, strategy: :insert end factory :user do name { "John Doe" } end end end it "uses the overridden create strategy to create the association" do FactoryBot.register_strategy(:insert, custom_strategy) post = FactoryBot.build(:post) expect(post.user.name).to eq "Custom strategy" end end factory_bot-6.5.6/spec/acceptance/reload_spec.rb000066400000000000000000000005111507676517300217060ustar00rootroot00000000000000describe "reload" do it "does not reset the value of use_parent_strategy" do custom_strategy = :custom_use_parent_strategy_value with_temporary_assignment(FactoryBot, :use_parent_strategy, custom_strategy) do FactoryBot.reload expect(FactoryBot.use_parent_strategy).to eq custom_strategy end end end factory_bot-6.5.6/spec/acceptance/sequence_context_spec.rb000066400000000000000000000133621507676517300240240ustar00rootroot00000000000000describe "sequences are evaluated in the correct context, directly & implicitly" do include FactoryBot::Syntax::Methods before do define_class("User") do attr_accessor :id, :name def awesome "aw yeah" end end end it "builds a sequence calling sprintf correctly" do FactoryBot.define do factory :sequence_with_sprintf, class: User do sequence(:id) { |n| sprintf("foo%d", n) } end end expect(FactoryBot.build(:sequence_with_sprintf).id).to eq "foo1" expect(build(:sequence_with_sprintf).id).to eq "foo2" end it "invokes the correct method on the instance" do FactoryBot.define do factory :sequence_with_public_method, class: User do sequence(:id) { public_method(:awesome).call } end end expect(FactoryBot.build(:sequence_with_public_method).id).to eq "aw yeah" expect(build(:sequence_with_public_method).id).to eq "aw yeah" end it "invokes a method with no arguments on the instance" do FactoryBot.define do factory :sequence_with_frozen, class: User do sequence(:id) { frozen? } end end expect(FactoryBot.build(:sequence_with_frozen).id).to be false expect(build(:sequence_with_frozen).id).to be false end it "allows direct reference of a method in a sequence" do FactoryBot.define do factory :sequence_referencing_attribute_directly, class: User do sequence(:id) { |n| "#{awesome}#{n}" } end end expect(FactoryBot.build(:sequence_referencing_attribute_directly).id).to eq "aw yeah1" expect(build(:sequence_referencing_attribute_directly).id).to eq "aw yeah2" end context "with inherited factories" do it "uses the parent's sequenced attribute" do FactoryBot.define do factory :parent, class: User do sequence(:id) { |n| "id_#{n}" } factory :child, class: User end end parents = FactoryBot.build_list(:parent, 3) expect(parents[0].id).to eq "id_1" expect(parents[1].id).to eq "id_2" expect(parents[2].id).to eq "id_3" children = build_list(:child, 3) expect(children[0].id).to eq "id_4" expect(children[1].id).to eq "id_5" expect(children[2].id).to eq "id_6" end it "invokes the parent's sequenced trait from within a child's inherited trait" do FactoryBot.define do sequence :global_seq factory :parent, class: User do trait :with_sequenced_id do sequence(:id) { |n| "id_#{n}" } end factory :child, class: User end end parent_ids = FactoryBot.build_list(:parent, 3, :with_sequenced_id).map(&:id) expect(parent_ids).to eq ["id_1", "id_2", "id_3"] child_ids = build_list(:child, 3, :with_sequenced_id).map(&:id) expect(child_ids).to eq ["id_4", "id_5", "id_6"] end it "invokes the child's sequenced trait from within the child's own trait" do FactoryBot.define do sequence :global_sequence factory :parent, class: User do trait :with_sequenced_id do sequence(:id) { |n| "id_#{n}" } end factory :child, class: User, aliases: [:toddler, :teen] do sequence(:id) { |n| "id_#{n}" } trait :with_own_sequence do sequence(:id, 1000, aliases: [:woo_hoo, :woo_hoo_2]) { |n| "id_#{n}" } end end end end parents = FactoryBot.build_list(:parent, 3, :with_sequenced_id) expect(parents[0].id).to eq "id_1" expect(parents[1].id).to eq "id_2" expect(parents[2].id).to eq "id_3" children = build_list(:child, 3) expect(children[0].id).to eq "id_1" expect(children[1].id).to eq "id_2" expect(children[2].id).to eq "id_3" children = FactoryBot.build_list(:child, 3, :with_sequenced_id, :with_own_sequence) expect(children[0].id).to eq "id_1000" expect(children[1].id).to eq "id_1001" expect(children[2].id).to eq "id_1002" end it "redefines a child's sequence" do FactoryBot.define do factory :parent, class: User do sequence(:id) { |n| "parent_#{n}" } factory :child, class: User do sequence(:id) { |n| "child_#{n}" } end end end parents = FactoryBot.build_list(:parent, 3) expect(parents[0].id).to eq "parent_1" expect(parents[1].id).to eq "parent_2" expect(parents[2].id).to eq "parent_3" children = build_list(:child, 3) expect(children[0].id).to eq "child_1" expect(children[1].id).to eq "child_2" expect(children[2].id).to eq "child_3" end it "maintains context separation" do FactoryBot.define do sequence(:id) { |n| "global_#{n}" } factory :parent, class: User do sequence(:id) { |n| "parent_#{n}" } factory :child, class: User do sequence(:id) { |n| "child_#{n}" } end end factory :sibling, class: User do sequence(:id) { |n| "sibling_#{n}" } end end globals = FactoryBot.generate_list(:id, 3) expect(globals[0]).to eq "global_1" expect(globals[1]).to eq "global_2" expect(globals[2]).to eq "global_3" parents = build_list(:parent, 3) expect(parents[0].id).to eq "parent_1" expect(parents[1].id).to eq "parent_2" expect(parents[2].id).to eq "parent_3" children = FactoryBot.build_list(:child, 3) expect(children[0].id).to eq "child_1" expect(children[1].id).to eq "child_2" expect(children[2].id).to eq "child_3" siblings = build_list(:sibling, 3) expect(siblings[0].id).to eq "sibling_1" expect(siblings[1].id).to eq "sibling_2" expect(siblings[2].id).to eq "sibling_3" end end end factory_bot-6.5.6/spec/acceptance/sequence_setting_spec.rb000066400000000000000000000300551507676517300240130ustar00rootroot00000000000000describe "FactoryBot.set_sequence" do include FactoryBot::Syntax::Methods describe "on success" do describe "setting sequence to correct value" do it "works with Integer sequences" do FactoryBot.define do sequence(:email) { |n| "somebody#{n}@example.com" } end expect(generate_list(:email, 3).last).to eq "somebody3@example.com" FactoryBot.set_sequence(:email, 54321) expect(generate_list(:email, 3).last).to eq "somebody54323@example.com" end it "works with negative Integer sequences" do FactoryBot.define do sequence(:email, -50) { |n| "somebody#{n}@example.com" } end expect(generate_list(:email, 3).last).to eq "somebody-48@example.com" FactoryBot.set_sequence(:email, -25) expect(generate_list(:email, 3).last).to eq "somebody-23@example.com" end it "works with Enumerable sequences" do define_class("User") { attr_accessor :name } FactoryBot.define do factory :user do sequence(:name, %w[Jane Joe Josh Jayde John].to_enum) end end expect(generate(:user, :name)).to eq "Jane" FactoryBot.set_sequence(:user, :name, "Jayde") expect(generate(:user, :name)).to eq "Jayde" expect(generate(:user, :name)).to eq "John" end it "works with String sequences" do define_class("User") { attr_accessor :initial } FactoryBot.define do factory :user do factory :pilot do sequence(:initial, "a") end end end expect(generate_list(:pilot, :initial, 3)).to eq ["a", "b", "c"] FactoryBot.set_sequence(:pilot, :initial, "z") expect(generate_list(:pilot, :initial, 3)).to eq ["z", "aa", "ab"] end it "works with Date sequences" do define_class("User") { attr_accessor :dob } FactoryBot.define do factory :user do factory :pilot do factory :jet_pilot do sequence(:dob, Date.parse("2025-04-01")) end end end end expect(generate(:jet_pilot, :dob)).to eq Date.parse("2025-04-01") FactoryBot.set_sequence(:jet_pilot, :dob, Date.parse("2025-05-01")) expect(generate_list(:jet_pilot, :dob, 3).last).to eq Date.parse("2025-05-03") end it "works with lazy Integer sequences" do FactoryBot.define do sequence(:email, proc { 42 }) { |n| "somebody#{n}@example.com" } end expect(generate_list(:email, 3).last).to eq "somebody44@example.com" FactoryBot.set_sequence(:email, 54321) expect(generate_list(:email, 3).last).to eq "somebody54323@example.com" end it "does not collide with other factory or global sequences" do define_class("User") { attr_accessor :email } define_class("Admin") { attr_accessor :email } FactoryBot.define do sequence(:email) { |n| "global#{n}@example.com" } factory :user do sequence(:email) { |n| "user#{n}@example.com" } factory :admin do sequence(:email) { |n| "admin#{n}@example.com" } end end end generate_list :email, 2 generate_list :user, :email, 2 generate_list :admin, :email, 2 expect(generate(:email)).to eq "global3@example.com" expect(generate(:user, :email)).to eq "user3@example.com" expect(generate(:admin, :email)).to eq "admin3@example.com" FactoryBot.set_sequence(:user, :email, 22222) FactoryBot.set_sequence(:admin, :email, 33333) expect(generate(:email)).to eq "global4@example.com" expect(generate(:user, :email)).to eq "user22222@example.com" expect(generate(:admin, :email)).to eq "admin33333@example.com" end end describe "sequence targeting by URI" do before do define_class("User") FactoryBot.define do sequence :counter trait :global_trait do sequence :counter end factory :parent, class: "User" do sequence :counter trait :parent_trait do sequence :counter end factory :child do sequence :counter trait :child_trait do sequence :counter end end end end end it "accepts symbolic URIs" do expect(generate(:counter)).to eq 1 expect(generate(:global_trait, :counter)).to eq 1 expect(generate(:parent, :counter)).to eq 1 expect(generate(:parent, :parent_trait, :counter)).to eq 1 expect(generate(:child, :counter)).to eq 1 expect(generate(:child, :child_trait, :counter)).to eq 1 FactoryBot.set_sequence :counter, 1000 FactoryBot.set_sequence :global_trait, :counter, 3000 FactoryBot.set_sequence :parent, :counter, 6000 FactoryBot.set_sequence :parent, :parent_trait, :counter, 9000 FactoryBot.set_sequence :child, :counter, 12_000 FactoryBot.set_sequence :child, :child_trait, :counter, 15_000 expect(generate(:counter)).to eq 1000 expect(generate(:global_trait, :counter)).to eq 3000 expect(generate(:parent, :counter)).to eq 6000 expect(generate(:parent, :parent_trait, :counter)).to eq 9000 expect(generate(:child, :counter)).to eq 12_000 expect(generate(:child, :child_trait, :counter)).to eq 15_000 end it "accepts string URIs" do expect(generate("counter")).to eq 1 expect(generate("global_trait/counter")).to eq 1 expect(generate("parent/counter")).to eq 1 expect(generate("parent/parent_trait/counter")).to eq 1 expect(generate("child/counter")).to eq 1 expect(generate("child/child_trait/counter")).to eq 1 FactoryBot.set_sequence "counter", 1000 FactoryBot.set_sequence "global_trait/counter", 3000 FactoryBot.set_sequence "parent/counter", 6000 FactoryBot.set_sequence "parent/parent_trait/counter", 9000 FactoryBot.set_sequence "child/counter", 12_000 FactoryBot.set_sequence "child/child_trait/counter", 15_000 expect(generate("counter")).to eq 1000 expect(generate("global_trait/counter")).to eq 3000 expect(generate("parent/counter")).to eq 6000 expect(generate("parent/parent_trait/counter")).to eq 9000 expect(generate("child/counter")).to eq 12_000 expect(generate("child/child_trait/counter")).to eq 15_000 end end describe "name format support" do it "accepts String or Symbol sequence names" do FactoryBot.define do sequence(:email) { |n| "user#{n}@example.com" } end FactoryBot.set_sequence(:email, 54321) expect(generate(:email)).to eq "user54321@example.com" FactoryBot.set_sequence("email", 777) expect(generate(:email)).to eq "user777@example.com" end it "accepts String or Symbol factory names" do define_class("User") { attr_accessor :email } FactoryBot.define do factory :user do sequence(:email) { |n| "user#{n}@example.com" } end end FactoryBot.set_sequence(:user, :email, 54321) expect(generate(:user, :email)).to eq "user54321@example.com" FactoryBot.set_sequence("user", "email", 777) expect(generate("user", "email")).to eq "user777@example.com" end end describe "alias support" do it "works with aliases for both sequence and factory" do define_class("User") { attr_accessor :email } FactoryBot.define do factory :user, aliases: [:author, :commenter] do sequence(:email, aliases: ["primary_email", :alt_email]) { |n| "user#{n}@example.com" } end end generate_list :user, :email, 2 expect(generate(:user, :email)).to eq "user3@example.com" FactoryBot.set_sequence(:user, :email, 11111) expect(generate(:user, :email)).to eq "user11111@example.com" FactoryBot.set_sequence(:author, :primary_email, 22222) expect(generate(:user, :email)).to eq "user22222@example.com" FactoryBot.set_sequence(:commenter, :alt_email, 33333) expect(generate(:user, :email)).to eq "user33333@example.com" end end end describe "error handling" do describe "unknown sequence names" do it "raises an error for unknown global sequences" do expect { FactoryBot.set_sequence(:test, 54321) } .to raise_error KeyError, /Sequence not registered: 'test'/ end it "raises an error for unknown factory sequences" do FactoryBot.define do factory :user do sequence(:email) { |n| "alt_user#{n}@example.com" } end end expect { FactoryBot.set_sequence(:user, :test, 54321) } .to raise_error KeyError, /Sequence not registered: 'user\/test'/ end it "raises an error when factory sequence doesn't exist but global does" do FactoryBot.define do sequence(:email) { |n| "global#{n}@example.com" } factory :user do sequence(:alt_email) { |n| "alt_user#{n}@example.com" } end end expect { FactoryBot.set_sequence(:user, :email, 54321) } .to raise_error KeyError, /Sequence not registered: 'user\/email'/ end it "raises an error for inherited sequences" do define_class("User") { attr_accessor :email } FactoryBot.define do sequence(:email) { |n| "global#{n}@example.com" } factory :user do sequence(:email) { |n| "user#{n}@example.com" } factory :admin end end admin = build(:admin) expect(admin.email).to eq "user1@example.com" expect { FactoryBot.set_sequence(:admin, :email, 54321) } .to raise_error KeyError, /Sequence not registered: 'admin\/email'/ end end describe "invalid values" do it "raises an error when value is below minimum for Integer sequences" do FactoryBot.define do sequence(:counter, 1000) end expect { FactoryBot.set_sequence(:counter, 999) } .to raise_error ArgumentError, /Value cannot be less than: 1000/ end it "raises an error for unmatched String values", :slow do FactoryBot.define do sequence(:char, "c") end expect { FactoryBot.set_sequence(:char, "a") } .to raise_error ArgumentError, /Unable to find 'a' in the sequence/ end it "raises an error for unmatched Enumerable values" do names = %w[Jane Joe Josh Jayde John].to_enum allow_any_instance_of(FactoryBot::Sequence).to receive(:can_set_value_by_index?).and_return(false) FactoryBot.define do sequence(:name, names) end expect { FactoryBot.set_sequence(:name, "Jester") } .to raise_error ArgumentError, /Unable to find 'Jester' in the sequence/ end it "times out when value cannot be found within timeout period", :slow do with_temporary_assignment(FactoryBot, :sequence_setting_timeout, 3) do FactoryBot.define do sequence(:test, "a") end start = Time.now expect { FactoryBot.set_sequence(:test, "zzzzzzzzzz") } .to raise_error ArgumentError, /Unable to find 'zzzzzzzzzz' in the sequence/ duration = Time.now - start expect(duration >= 3.seconds).to be_truthy expect(duration < 4.seconds).to be_truthy end end it "leaves sequence unchanged when value is not found" do FactoryBot.define do sequence(:name, %w[Jane Joe Josh Jayde John].to_enum) end generate_list :name, 2 expect(generate(:name)).to eq "Josh" expect { FactoryBot.set_sequence(:name, "Jester") } .to raise_error ArgumentError, /Unable to find 'Jester' in the sequence/ expect(generate(:name)).to eq "Jayde" end end end end factory_bot-6.5.6/spec/acceptance/sequence_spec.rb000066400000000000000000000154011507676517300222540ustar00rootroot00000000000000describe "sequences" do include FactoryBot::Syntax::Methods require "ostruct" # = On Success # ====================================================================== # describe "on success" do it "generates several values in the correct format" do define_class("User") { attr_accessor :email } FactoryBot.define do sequence(:email) { |n| "global-#{n}@example.com" } factory :user do sequence(:email) { |n| "user-#{n}@example.com" } end end expect(generate(:email)).to eq "global-1@example.com" expect(generate(:email)).to eq "global-2@example.com" expect(generate(:email)).to eq "global-3@example.com" expect(generate(:user, :email)).to eq "user-1@example.com" expect(generate(:user, :email)).to eq "user-2@example.com" expect(generate(:user, :email)).to eq "user-3@example.com" end it "generates sequential numbers if no block is given" do define_class("User") { attr_accessor :email } FactoryBot.define do sequence :global_order factory :user do sequence :user_order end end expect(generate(:global_order)).to eq 1 expect(generate(:global_order)).to eq 2 expect(generate(:global_order)).to eq 3 expect(generate(:user, :user_order)).to eq 1 expect(generate(:user, :user_order)).to eq 2 expect(generate(:user, :user_order)).to eq 3 end it "generates aliases for the sequence that reference the same block" do define_class("User") { attr_accessor :email } FactoryBot.define do sequence(:size, aliases: [:count, :length]) { |n| "global-called-#{n}" } factory :user, aliases: [:author, :commenter] do sequence(:size, aliases: [:count, :length]) { |n| "user-called-#{n}" } end end expect(generate(:size)).to eq "global-called-1" expect(generate(:count)).to eq "global-called-2" expect(generate(:length)).to eq "global-called-3" expect(generate(:user, :size)).to eq "user-called-1" expect(generate(:author, :count)).to eq "user-called-2" expect(generate(:commenter, :length)).to eq "user-called-3" end it "generates aliases for the sequence that reference the same block and retains value" do define_class("User") { attr_accessor :email } FactoryBot.define do sequence(:size, "a", aliases: [:count, :length]) { |n| "global-called-#{n}" } factory :user, aliases: [:author, :commenter] do sequence(:size, "x", aliases: [:count, :length]) { |n| "user-called-#{n}" } end end expect(generate(:size)).to eq "global-called-a" expect(generate(:count)).to eq "global-called-b" expect(generate(:length)).to eq "global-called-c" expect(generate(:user, :size)).to eq "user-called-x" expect(generate(:author, :count)).to eq "user-called-y" expect(generate(:commenter, :length)).to eq "user-called-z" end it "generates sequences after lazy loading an initial value from a proc" do loaded = false FactoryBot.define do sequence :count, proc { loaded = true "d" } end expect(loaded).to be false first_value = generate(:count) another_value = generate(:count) expect(loaded).to be true expect(first_value).to eq "d" expect(another_value).to eq "e" end it "generates sequences after lazy loading an initial value from an object responding to call" do define_class("HasCallMethod") do def initialise @called = false end def called? @called end def call @called = true "ABC" end end has_call_method_instance = HasCallMethod.new FactoryBot.define do sequence :letters, has_call_method_instance end expect(has_call_method_instance).not_to be_called first_value = generate(:letters) another_value = generate(:letters) expect(has_call_method_instance).to be_called expect(first_value).to eq "ABC" expect(another_value).to eq "ABD" end it "generates few values of the sequence" do define_class("User") { attr_accessor :email } FactoryBot.define do sequence(:email) { |n| "global-#{n}@example.com" } factory :user do sequence(:email) { |n| "user-#{n}@example.com" } end end global_values = generate_list(:email, 3) expect(global_values[0]).to eq "global-1@example.com" expect(global_values[1]).to eq "global-2@example.com" expect(global_values[2]).to eq "global-3@example.com" user_values = generate_list(:user, :email, 3) expect(user_values[0]).to eq "user-1@example.com" expect(user_values[1]).to eq "user-2@example.com" expect(user_values[2]).to eq "user-3@example.com" end it "generates few values of the sequence with a given scope" do define_class("User") { attr_accessor :name, :email } FactoryBot.define do factory :user do sequence(:email) { |n| "#{name}-#{n}@example.com" } end end test_scope = OpenStruct.new(name: "Jester") user_values = generate_list(:user, :email, 3, scope: test_scope) expect(user_values[0]).to eq "Jester-1@example.com" expect(user_values[1]).to eq "Jester-2@example.com" expect(user_values[2]).to eq "Jester-3@example.com" end end # "on success" # = On Failure # ====================================================================== # describe "on failure" do it "it fails with an unknown sequence or factory name" do define_class("User") { attr_accessor :email } FactoryBot.define do sequence :counter factory :user do sequence counter end end expect { generate(:test).to raise_error KeyError, /Sequence not registered: :test/ } expect { generate(:user, :test).to raise_error KeyError, /Sequence not registered: user:test/ } expect { generate(:admin, :counter).to raise_error KeyError, /Sequence not registered: "admin:counter"/ } end it "it fails with a sequence that references a scoped attribute, but no scope given" do define_class("User") { attr_accessor :name, :age, :info } FactoryBot.define do factory :user do sequence(:info) { |n| "#{name}:#{age + n}" } end end jester = FactoryBot.build(:user, name: "Jester", age: 21) expect(generate(:user, :info, scope: jester)).to eq "Jester:23" expect { generate(:user, :info) } .to raise_error ArgumentError, "Sequence 'user/info' failed to return a value. " \ "Perhaps it needs a scope to operate? (scope: )" end end # "on failure" end factory_bot-6.5.6/spec/acceptance/skip_create_spec.rb000066400000000000000000000005441507676517300227370ustar00rootroot00000000000000describe "skipping the default create" do before do define_model("User", email: :string) FactoryBot.define do factory :user do skip_create email { "john@example.com" } end end end it "doesn't execute anything when creating the instance" do expect(FactoryBot.create(:user)).not_to be_persisted end end factory_bot-6.5.6/spec/acceptance/stub_spec.rb000066400000000000000000000076451507676517300214340ustar00rootroot00000000000000describe "a stubbed instance" do include FactoryBot::Syntax::Methods before do define_model("User") define_model("Post", user_id: :integer) do belongs_to :user end FactoryBot.define do factory :user factory :post do user end end end subject { build_stubbed(:post) } it "acts as if it came from the database" do should_not be_new_record end it "assigns associations and acts as if it is saved" do expect(subject.user).to be_kind_of(User) expect(subject.user).not_to be_new_record end end describe "a stubbed instance with timestamps" do include FactoryBot::Syntax::Methods before do define_model("ModelWithTimestamps", created_at: :datetime, updated_at: :datetime) FactoryBot.define do factory :model_with_timestamps end end subject { build_stubbed(:model_with_timestamps) } it "assigns the exact same datetime" do expect(subject.created_at).to eq(subject.updated_at) end end describe "a stubbed instance overriding strategy" do include FactoryBot::Syntax::Methods before do define_model("User") define_model("Post", user_id: :integer) do belongs_to :user end FactoryBot.define do factory :user factory :post do association(:user, strategy: :build) end end end subject { build_stubbed(:post) } it "acts as if it is saved in the database" do should_not be_new_record end it "assigns associations and acts as if it is saved" do expect(subject.user).to be_kind_of(User) expect(subject.user).not_to be_new_record end end describe "overridden primary keys conventions" do describe "a stubbed instance with a uuid primary key" do it "builds a stubbed instance" do using_model("ModelWithUuid", primary_key: :uuid) do FactoryBot.define do factory :model_with_uuid end model = FactoryBot.build_stubbed(:model_with_uuid) expect(model).to be_truthy end end it "behaves like a persisted record" do using_model("ModelWithUuid", primary_key: :uuid) do FactoryBot.define do factory :model_with_uuid end model = FactoryBot.build_stubbed(:model_with_uuid) expect(model).to be_persisted expect(model).not_to be_new_record end end it "has a uuid primary key" do using_model("ModelWithUuid", primary_key: :uuid) do FactoryBot.define do factory :model_with_uuid end model = FactoryBot.build_stubbed(:model_with_uuid) expect(model.id).to be_a(String) end end end describe "a stubbed instance with no primary key" do it "builds a stubbed instance" do using_model("ModelWithoutPk", primary_key: false) do FactoryBot.define do factory :model_without_pk end model = FactoryBot.build_stubbed(:model_without_pk) expect(model).to be_truthy end end it "behaves like a persisted record" do using_model("ModelWithoutPk", primary_key: false) do FactoryBot.define do factory :model_without_pk end model = FactoryBot.build_stubbed(:model_without_pk) expect(model).to be_persisted expect(model).not_to be_new_record end end end describe "a stubbed instance with no id setter" do it "builds a stubbed instance" do FactoryBot.define do factory :model_hash, class: Hash end model = FactoryBot.build_stubbed(:model_hash) expect(model).to be_truthy end end def using_model(name, primary_key:) define_class(name, ActiveRecord::Base) connection = ActiveRecord::Base.connection begin clear_generated_table(name.tableize) connection.create_table(name.tableize, id: primary_key) do |t| t.column :updated_at, :datetime end yield ensure clear_generated_table(name.tableize) end end end factory_bot-6.5.6/spec/acceptance/syntax_methods_within_dynamic_attributes_spec.rb000066400000000000000000000025661507676517300310610ustar00rootroot00000000000000describe "syntax methods within dynamic attributes" do before do define_model("Post", title: :string, user_id: :integer) do belongs_to :user def generate "generate result" end end define_model("User", email: :string) FactoryBot.define do sequence(:email_address) { |n| "person-#{n}@example.com" } factory :user do email { generate(:email_address) } end factory :post do title { generate } user { build(:user) } end end end it "can access syntax methods from dynamic attributes" do expect(FactoryBot.build(:user).email).to eq "person-1@example.com" expect(FactoryBot.attributes_for(:user)[:email]).to eq "person-2@example.com" end it "can access syntax methods from dynamic attributes" do expect(FactoryBot.build(:post).user).to be_instance_of(User) end it "can access methods already existing on the class" do expect(FactoryBot.build(:post).title).to eq "generate result" expect(FactoryBot.attributes_for(:post)[:title]).to be_nil end it "allows syntax methods to be used when the block has an arity of 1" do FactoryBot.define do factory :post_using_block_with_variable, parent: :post do user { |_| build(:user) } end end expect(FactoryBot.build(:post_using_block_with_variable).user).to be_instance_of(User) end end factory_bot-6.5.6/spec/acceptance/traits_spec.rb000066400000000000000000000614541507676517300217630ustar00rootroot00000000000000describe "an instance generated by a factory with multiple traits" do before do define_model("User", name: :string, admin: :boolean, gender: :string, email: :string, date_of_birth: :date, great: :string) FactoryBot.define do factory :user do name { "John" } trait :great do great { "GREAT!!!" } end trait :great do great { "EVEN GREATER!!!" } end trait :admin do admin { true } end trait :admin_trait do admin { true } end trait :male do name { "Joe" } gender { "Male" } end trait :female do name { "Jane" } gender { "Female" } end trait :make_it_great do great end factory :great_user do great end factory :even_greater_user do great trait :great do great { "EVEN GREATER!!!" } end end factory :greatest_user do trait :great do great { "GREATEST EVER!!!" } end end factory :admin, traits: [:admin] factory :male_user do male factory :child_male_user do date_of_birth { Date.parse("1/1/2000") } end end factory :female, traits: [:female] do trait :admin do admin { true } name { "Judy" } end factory :female_great_user do great end factory :female_admin_judy, traits: [:admin] end factory :female_admin, traits: [:female, :admin] factory :female_after_male_admin, traits: [:male, :female, :admin] factory :male_after_female_admin, traits: [:female, :male, :admin] end trait :email do email { "#{name}@example.com" } end factory :user_with_email, class: User, traits: [:email] do name { "Bill" } end end end context "the parent class" do subject { FactoryBot.create(:user) } its(:name) { should eq "John" } its(:gender) { should be_nil } it { should_not be_admin } end context "the child class with one trait" do subject { FactoryBot.create(:admin) } its(:name) { should eq "John" } its(:gender) { should be_nil } it { should be_admin } end context "the other child class with one trait" do subject { FactoryBot.create(:female) } its(:name) { should eq "Jane" } its(:gender) { should eq "Female" } it { should_not be_admin } end context "the child with multiple traits" do subject { FactoryBot.create(:female_admin) } its(:name) { should eq "Jane" } its(:gender) { should eq "Female" } it { should be_admin } end context "the child with multiple traits and overridden attributes" do subject { FactoryBot.create(:female_admin, name: "Jill", gender: nil) } its(:name) { should eq "Jill" } its(:gender) { should be_nil } it { should be_admin } end context "the child with multiple traits who override the same attribute" do context "when the male assigns name after female" do subject { FactoryBot.create(:male_after_female_admin) } its(:name) { should eq "Joe" } its(:gender) { should eq "Male" } it { should be_admin } end context "when the female assigns name after male" do subject { FactoryBot.create(:female_after_male_admin) } its(:name) { should eq "Jane" } its(:gender) { should eq "Female" } it { should be_admin } end end context "child class with scoped trait and inherited trait" do subject { FactoryBot.create(:female_admin_judy) } its(:name) { should eq "Judy" } its(:gender) { should eq "Female" } it { should be_admin } end context "factory using global trait" do subject { FactoryBot.create(:user_with_email) } its(:name) { should eq "Bill" } its(:email) { should eq "Bill@example.com" } end context "factory created with alternate syntax for specifying trait" do subject { FactoryBot.create(:male_user) } its(:gender) { should eq "Male" } context "where trait name and attribute are the same" do subject { FactoryBot.create(:great_user) } its(:great) { should eq "GREAT!!!" } end context "where trait name and attribute are the same and attribute is overridden" do subject { FactoryBot.create(:great_user, great: "SORT OF!!!") } its(:great) { should eq "SORT OF!!!" } end end context "factory with trait defined multiple times" do subject { FactoryBot.create(:great_user) } its(:great) { should eq "GREAT!!!" } context "child factory redefining trait" do subject { FactoryBot.create(:even_greater_user) } its(:great) { should eq "EVEN GREATER!!!" } end end context "factory with implicit traits called by child" do it "calls the correct trait when parent built first" do user = FactoryBot.create(:user, :make_it_great) expect(user.great).to eq "GREAT!!!" end it "calls the correct trait when child built first" do greatest = FactoryBot.create(:greatest_user, :make_it_great) user = FactoryBot.create(:user, :make_it_great) expect(user.great).to eq "GREAT!!!" expect(greatest.great).to eq "GREATEST EVER!!!" end end context "child factory created where trait attributes are inherited" do subject { FactoryBot.create(:child_male_user) } its(:gender) { should eq "Male" } its(:date_of_birth) { should eq Date.parse("1/1/2000") } end context "child factory using grandparents' trait" do subject { FactoryBot.create(:female_great_user) } its(:great) { should eq "GREAT!!!" } end end describe "trait indifferent access" do context "when trait is defined as a string" do it "can be invoked with a string" do build_user_factory_with_admin_trait("admin") user = FactoryBot.build(:user, "admin") expect(user).to be_admin end it "can be invoked with a symbol" do build_user_factory_with_admin_trait("admin") user = FactoryBot.build(:user, :admin) expect(user).to be_admin end end context "when trait is defined as a symbol" do it "can be invoked with a string" do build_user_factory_with_admin_trait(:admin) user = FactoryBot.build(:user, "admin") expect(user).to be_admin end it "can be invoked with a symbol" do build_user_factory_with_admin_trait(:admin) user = FactoryBot.build(:user, :admin) expect(user).to be_admin end end context "when trait is defined as integer" do it "can be invoked with a string" do build_user_factory_with_admin_trait(42) user = FactoryBot.build(:user, "42") expect(user).to be_admin end it "can be invoked with as integer" do build_user_factory_with_admin_trait(42) user = FactoryBot.build(:user, 42) expect(user).to be_admin end end context "when trait is defined as struct" do it "can be invoked with a string" do instance = Struct.new(:a, :b).new(1, "x") build_user_factory_with_admin_trait(instance) user = FactoryBot.build(:user, '#') expect(user).to be_admin end it "can be invoked with a struct" do instance = Struct.new(:a, :b).new(1, "x") build_user_factory_with_admin_trait(instance) user = FactoryBot.build(:user, instance) expect(user).to be_admin end end def build_user_factory_with_admin_trait(trait_name) define_model("User", admin: :boolean) FactoryBot.define do factory :user do admin { false } trait trait_name do admin { true } end end end end end describe "looking up traits that don't exist" do context "when passing an invalid override trait" do it "raises a KeyError" do define_class("User") FactoryBot.define do factory :user end expect { FactoryBot.build(:user, double("not a trait")) } .to raise_error(KeyError) end end context "when the factory includes an invalid default trait" do it "raises a KeyError including the factory name" do define_class("User") FactoryBot.define do factory :user do inaccessible_trait end factory :some_other_factory do trait :inaccessible_trait end end expect { FactoryBot.build(:user) }.to raise_error( KeyError, 'Trait not registered: "inaccessible_trait" referenced within "user" definition' ) end it "maintains 'Did you mean?' suggestions at the end of the error message" do define_class("User") FactoryBot.define do trait :not_quit factory :user do not_quite end end expect { FactoryBot.build(:user) }.to raise_did_you_mean_error end end context "when a trait includes an invalid default trait" do it "raises a KeyError including the factory name" do define_class("User") FactoryBot.define do factory :user do trait :admin do inaccessible_trait end end factory :some_other_factory do trait :inaccessible_trait end end expect { FactoryBot.build(:user, :admin) }.to raise_error( KeyError, 'Trait not registered: "inaccessible_trait" referenced within "admin" definition' ) end end end describe "traits with callbacks" do before do define_model("User", name: :string) FactoryBot.define do factory :user do name { "John" } trait :great do after(:create) { |user| user.name.upcase! } end trait :awesome do after(:create) { |user| user.name = "awesome" } end factory :caps_user, traits: [:great] factory :awesome_user, traits: [:great, :awesome] factory :caps_user_implicit_trait do great end end end end context "when the factory has a trait passed via arguments" do subject { FactoryBot.create(:caps_user) } its(:name) { should eq "JOHN" } end context "when the factory has an implicit trait" do subject { FactoryBot.create(:caps_user_implicit_trait) } its(:name) { should eq "JOHN" } end it "executes callbacks in the order assigned" do expect(FactoryBot.create(:awesome_user).name).to eq "awesome" end end describe "traits added via strategy" do before do define_model("User", name: :string, admin: :boolean) FactoryBot.define do factory :user do name { "John" } trait :admin do admin { true } end trait :great do after(:create) { |user| user.name.upcase! } end end end end context "adding traits in create" do subject { FactoryBot.create(:user, :admin, :great, name: "Joe") } its(:admin) { should be true } its(:name) { should eq "JOE" } it "doesn't modify the user factory" do subject expect(FactoryBot.create(:user)).not_to be_admin expect(FactoryBot.create(:user).name).to eq "John" end end context "adding traits in build" do subject { FactoryBot.build(:user, :admin, :great, name: "Joe") } its(:admin) { should be true } its(:name) { should eq "Joe" } end context "adding traits in attributes_for" do subject { FactoryBot.attributes_for(:user, :admin, :great) } its([:admin]) { should be true } its([:name]) { should eq "John" } end context "adding traits in build_stubbed" do subject { FactoryBot.build_stubbed(:user, :admin, :great, name: "Jack") } its(:admin) { should be true } its(:name) { should eq "Jack" } end context "adding traits in create_list" do subject { FactoryBot.create_list(:user, 2, :admin, :great, name: "Joe") } its(:length) { should eq 2 } it "creates all the records" do subject.each do |record| expect(record.admin).to be true expect(record.name).to eq "JOE" end end end context "adding traits in build_list" do subject { FactoryBot.build_list(:user, 2, :admin, :great, name: "Joe") } its(:length) { should eq 2 } it "builds all the records" do subject.each do |record| expect(record.admin).to be true expect(record.name).to eq "Joe" end end end end describe "traits and dynamic attributes that are applied simultaneously" do before do define_model("User", name: :string, email: :string, combined: :string) FactoryBot.define do trait :email do email { "#{name}@example.com" } end factory :user do name { "John" } email combined { "#{name} <#{email}>" } end end end subject { FactoryBot.build(:user) } its(:name) { should eq "John" } its(:email) { should eq "John@example.com" } its(:combined) { should eq "John " } end describe "applying inline traits" do before do define_model("User") do has_many :posts end define_model("Post", user_id: :integer) do belongs_to :user end FactoryBot.define do factory :user do trait :with_post do posts { [Post.new] } end end end end it "applies traits only to the instance generated for that call" do expect(FactoryBot.create(:user, :with_post).posts).not_to be_empty expect(FactoryBot.create(:user).posts).to be_empty expect(FactoryBot.create(:user, :with_post).posts).not_to be_empty end end describe "inline traits overriding existing attributes" do before do define_model("User", status: :string) FactoryBot.define do factory :user do status { "pending" } trait(:accepted) { status { "accepted" } } trait(:declined) { status { "declined" } } factory :declined_user, traits: [:declined] factory :extended_declined_user, traits: [:declined] do status { "extended_declined" } end end end end it "returns the default status" do expect(FactoryBot.build(:user).status).to eq "pending" end it "prefers inline trait attributes over default attributes" do expect(FactoryBot.build(:user, :accepted).status).to eq "accepted" end it "prefers traits on a factory over default attributes" do expect(FactoryBot.build(:declined_user).status).to eq "declined" end it "prefers inline trait attributes over traits on a factory" do expect(FactoryBot.build(:declined_user, :accepted).status).to eq "accepted" end it "prefers attributes on factories over attributes from non-inline traits" do expect(FactoryBot.build(:extended_declined_user).status).to eq "extended_declined" end it "prefers inline traits over attributes on factories" do expect(FactoryBot.build(:extended_declined_user, :accepted).status).to eq "accepted" end it "prefers overridden attributes over attributes from traits, inline traits, or attributes on factories" do user = FactoryBot.build(:extended_declined_user, :accepted, status: "completely overridden") expect(user.status).to eq "completely overridden" end end describe "making sure the factory is properly compiled the first time we want to instantiate it" do before do define_model("User", role: :string, gender: :string, age: :integer) FactoryBot.define do factory :user do trait(:female) { gender { "female" } } trait(:admin) { role { "admin" } } factory :female_user do female end end end end it "can honor traits on the very first call" do user = FactoryBot.build(:female_user, :admin, age: 30) expect(user.gender).to eq "female" expect(user.age).to eq 30 expect(user.role).to eq "admin" end end describe "traits with to_create" do before do define_model("User", name: :string) FactoryBot.define do factory :user do trait :with_to_create do to_create { |instance| instance.name = "to_create" } end factory :sub_user do to_create { |instance| instance.name = "sub" } factory :child_user end factory :sub_user_with_trait do with_to_create factory :child_user_with_trait end factory :sub_user_with_trait_and_override do with_to_create to_create { |instance| instance.name = "sub with trait and override" } factory :child_user_with_trait_and_override end end end end it "can apply to_create from traits" do expect(FactoryBot.create(:user, :with_to_create).name).to eq "to_create" end it "can apply to_create from the definition" do expect(FactoryBot.create(:sub_user).name).to eq "sub" expect(FactoryBot.create(:child_user).name).to eq "sub" end it "gives additional traits higher priority than to_create from the definition" do expect(FactoryBot.create(:sub_user, :with_to_create).name).to eq "to_create" expect(FactoryBot.create(:child_user, :with_to_create).name).to eq "to_create" end it "gives base traits normal priority" do expect(FactoryBot.create(:sub_user_with_trait).name).to eq "to_create" expect(FactoryBot.create(:child_user_with_trait).name).to eq "to_create" end it "gives base traits lower priority than overrides" do expect(FactoryBot.create(:sub_user_with_trait_and_override).name).to eq "sub with trait and override" expect(FactoryBot.create(:child_user_with_trait_and_override).name).to eq "sub with trait and override" end it "gives additional traits higher priority than base traits and factory definition" do FactoryBot.define do trait :overridden do to_create { |instance| instance.name = "completely overridden" } end end sub_user = FactoryBot.create(:sub_user_with_trait_and_override, :overridden) child_user = FactoryBot.create(:child_user_with_trait_and_override, :overridden) expect(sub_user.name).to eq "completely overridden" expect(child_user.name).to eq "completely overridden" end end describe "traits with initialize_with" do before do define_class("User") do attr_reader :name def initialize(name) @name = name end end FactoryBot.define do factory :user do trait :with_initialize_with do initialize_with { new("initialize_with") } end factory :sub_user do initialize_with { new("sub") } factory :child_user end factory :sub_user_with_trait do with_initialize_with factory :child_user_with_trait end factory :sub_user_with_trait_and_override do with_initialize_with initialize_with { new("sub with trait and override") } factory :child_user_with_trait_and_override end end end end it "can apply initialize_with from traits" do expect(FactoryBot.build(:user, :with_initialize_with).name).to eq "initialize_with" end it "can apply initialize_with from the definition" do expect(FactoryBot.build(:sub_user).name).to eq "sub" expect(FactoryBot.build(:child_user).name).to eq "sub" end it "gives additional traits higher priority than initialize_with from the definition" do expect(FactoryBot.build(:sub_user, :with_initialize_with).name).to eq "initialize_with" expect(FactoryBot.build(:child_user, :with_initialize_with).name).to eq "initialize_with" end it "gives base traits normal priority" do expect(FactoryBot.build(:sub_user_with_trait).name).to eq "initialize_with" expect(FactoryBot.build(:child_user_with_trait).name).to eq "initialize_with" end it "gives base traits lower priority than overrides" do expect(FactoryBot.build(:sub_user_with_trait_and_override).name).to eq "sub with trait and override" expect(FactoryBot.build(:child_user_with_trait_and_override).name).to eq "sub with trait and override" end it "gives additional traits higher priority than base traits and factory definition" do FactoryBot.define do trait :overridden do initialize_with { new("completely overridden") } end end sub_user = FactoryBot.build(:sub_user_with_trait_and_override, :overridden) child_user = FactoryBot.build(:child_user_with_trait_and_override, :overridden) expect(sub_user.name).to eq "completely overridden" expect(child_user.name).to eq "completely overridden" end end describe "nested implicit traits" do before do define_class("User") do attr_accessor :gender, :role attr_reader :name def initialize(name) @name = name end end end shared_examples_for "assigning data from traits" do it "assigns the correct values" do user = FactoryBot.create(:user, :female_admin) expect(user.gender).to eq "FEMALE" expect(user.role).to eq "ADMIN" expect(user.name).to eq "Jane Doe" end end context "defined outside the factory" do before do FactoryBot.define do trait :female do gender { "female" } to_create { |instance| instance.gender = instance.gender.upcase } end trait :jane_doe do initialize_with { new("Jane Doe") } end trait :admin do role { "admin" } after(:build) { |instance| instance.role = instance.role.upcase } end trait :female_admin do female admin jane_doe end factory :user end end it_should_behave_like "assigning data from traits" end context "defined inside the factory" do before do FactoryBot.define do factory :user do trait :female do gender { "female" } to_create { |instance| instance.gender = instance.gender.upcase } end trait :jane_doe do initialize_with { new("Jane Doe") } end trait :admin do role { "admin" } after(:build) { |instance| instance.role = instance.role.upcase } end trait :female_admin do female admin jane_doe end end end end it_should_behave_like "assigning data from traits" end end describe "implicit traits containing callbacks" do before do define_model("User", value: :integer) FactoryBot.define do factory :user do value { 0 } trait :trait_with_callback do after(:build) { |user| user.value += 1 } end factory :user_with_trait_with_callback do trait_with_callback end end end end it "only runs the callback once" do expect(FactoryBot.build(:user_with_trait_with_callback).value).to eq 1 end end describe "traits used in associations" do before do define_model("User", admin: :boolean, name: :string) define_model("Comment", user_id: :integer) do belongs_to :user end define_model("Order", creator_id: :integer) do belongs_to :creator, class_name: "User" end define_model("Post", author_id: :integer) do belongs_to :author, class_name: "User" end FactoryBot.define do factory :user do admin { false } trait :admin do admin { true } end end factory :post do association :author, factory: [:user, :admin], name: "John Doe" end factory :comment do association :user, :admin, name: "Joe Slick" end factory :order do association :creator, :admin, factory: :user, name: "Joe Creator" end end end it "allows assigning traits for the factory of an association" do author = FactoryBot.create(:post).author expect(author).to be_admin expect(author.name).to eq "John Doe" end it "allows inline traits with the default association" do user = FactoryBot.create(:comment).user expect(user).to be_admin expect(user.name).to eq "Joe Slick" end it "allows inline traits with a specific factory for an association" do creator = FactoryBot.create(:order).creator expect(creator).to be_admin expect(creator.name).to eq "Joe Creator" end end describe "when a self-referential trait is defined" do it "raises a TraitDefinitionError" do define_model("User", name: :string) FactoryBot.define do factory :user do trait :admin do admin end end end expect { FactoryBot.build(:user, :admin) }.to raise_error( FactoryBot::TraitDefinitionError, "Self-referencing trait 'admin'" ) end it "raises a TraitDefinitionError" do define_model("User", name: :string) FactoryBot.define do factory :user do trait :admin do admin name { "name" } end end end expect { FactoryBot.build(:user, :admin) }.to raise_error( FactoryBot::TraitDefinitionError, "Self-referencing trait 'admin'" ) end end factory_bot-6.5.6/spec/acceptance/transient_attributes_spec.rb000066400000000000000000000066671507676517300247370ustar00rootroot00000000000000describe "transient attributes" do before do define_model("User", name: :string, email: :string) FactoryBot.define do sequence(:name) { |n| "John #{n}" } factory :user do transient do four { 2 + 2 } rockstar { true } upcased { false } end name { "#{FactoryBot.generate(:name)}#{" - Rockstar" if rockstar}" } email { "#{name.downcase}#{four}@example.com" } after(:create) do |user, evaluator| user.name.upcase! if evaluator.upcased end end end end context "returning attributes for a factory" do subject { FactoryBot.attributes_for(:user, rockstar: true) } it { should_not have_key(:four) } it { should_not have_key(:rockstar) } it { should_not have_key(:upcased) } it { should have_key(:name) } it { should have_key(:email) } end context "with a transient variable assigned" do let(:rockstar) { FactoryBot.create(:user, rockstar: true, four: "1234") } let(:rockstar_with_name) { FactoryBot.create(:user, name: "Jane Doe", rockstar: true) } let(:upcased_rockstar) { FactoryBot.create(:user, rockstar: true, upcased: true) } let(:groupie) { FactoryBot.create(:user, rockstar: false) } it "generates the correct attributes on a rockstar" do expect(rockstar.name).to eq "John 1 - Rockstar" expect(rockstar.email).to eq "john 1 - rockstar1234@example.com" end it "generates the correct attributes on an upcased rockstar" do expect(upcased_rockstar.name).to eq "JOHN 1 - ROCKSTAR" expect(upcased_rockstar.email).to eq "john 1 - rockstar4@example.com" end it "generates the correct attributes on a groupie" do expect(groupie.name).to eq "John 1" expect(groupie.email).to eq "john 14@example.com" end it "generates the correct attributes on a rockstar with a name" do expect(rockstar_with_name.name).to eq "Jane Doe" expect(rockstar_with_name.email).to eq "jane doe4@example.com" end end context "without transient variables assigned" do let(:rockstar) { FactoryBot.create(:user) } it "uses the default value of the attribute" do expect(rockstar.name).to eq "John 1 - Rockstar" end end end describe "transient sequences" do before do define_model("User", name: :string) FactoryBot.define do factory :user do transient do sequence(:counter) end name { "John Doe #{counter}" } end end end it "increments sequences correctly" do expect(FactoryBot.build(:user).name).to eq "John Doe 1" expect(FactoryBot.build(:user).name).to eq "John Doe 2" end end describe "assigning values from a transient attribute" do before do define_class("User") do attr_accessor :foo_id, :foo_name end define_class("Foo") do attr_accessor :id, :name def initialize(id, name) @id = id @name = name end end FactoryBot.define do factory :user do transient do foo { Foo.new("id-of-foo", "name-of-foo") } end foo_id { foo.id } foo_name { foo.name } end end end it "does not ignore an _id attribute that is an alias for a transient attribute" do user = FactoryBot.build(:user, foo: Foo.new("passed-in-id-of-foo", "passed-in-name-of-foo")) expect(user.foo_id).to eq "passed-in-id-of-foo" expect(user.foo_name).to eq "passed-in-name-of-foo" end end factory_bot-6.5.6/spec/factory_bot/000077500000000000000000000000001507676517300173315ustar00rootroot00000000000000factory_bot-6.5.6/spec/factory_bot/aliases_spec.rb000066400000000000000000000016571507676517300223220ustar00rootroot00000000000000describe FactoryBot, "aliases" do it "for an attribute should include the original attribute and a version suffixed with '_id'" do aliases = FactoryBot.aliases_for(:test) expect(aliases).to include(:test, :test_id) end it "for a foreign key should include both the suffixed and un-suffixed variants" do aliases = FactoryBot.aliases_for(:test_id) expect(aliases).to include(:test, :test_id) end it "for an attribute which starts with an underscore should not include a non-underscored version" do aliases = FactoryBot.aliases_for(:_id) expect(aliases).not_to include(:id) end end describe FactoryBot, "after defining an alias" do it "the list of aliases should include a variant with no suffix at all, and one with an '_id' suffix" do FactoryBot.aliases << [/(.*)_suffix/, '\1'] aliases = FactoryBot.aliases_for(:test_suffix) expect(aliases).to include(:test, :test_suffix_id) end end factory_bot-6.5.6/spec/factory_bot/attribute/000077500000000000000000000000001507676517300213345ustar00rootroot00000000000000factory_bot-6.5.6/spec/factory_bot/attribute/association_spec.rb000066400000000000000000000021671507676517300252150ustar00rootroot00000000000000describe FactoryBot::Attribute::Association do let(:name) { :author } let(:factory) { :user } let(:overrides) { {first_name: "John"} } let(:association) { double("association") } subject { FactoryBot::Attribute::Association.new(name, factory, overrides) } before do # Define an '#association' instance method allowing it to be mocked. # Usually this is determined via '#method_missing' missing_methods = Module.new { def association(*args) end } subject.extend(missing_methods) allow(subject) .to receive(:association).with(any_args).and_return association end it { should be_association } its(:name) { should eq name } it "builds the association when calling the proc" do expect(subject.to_proc.call).to eq association end it "builds the association when calling the proc" do subject.to_proc.call expect(subject).to have_received(:association).with(factory, overrides) end end describe FactoryBot::Attribute::Association, "with a string name" do subject { FactoryBot::Attribute::Association.new("name", :user, {}) } its(:name) { should eq :name } end factory_bot-6.5.6/spec/factory_bot/attribute/dynamic_spec.rb000066400000000000000000000035111507676517300243170ustar00rootroot00000000000000describe FactoryBot::Attribute::Dynamic do let(:name) { :first_name } let(:block) { -> {} } subject { FactoryBot::Attribute::Dynamic.new(name, false, block) } its(:name) { should eq name } context "with a block returning a static value" do let(:block) { -> { "value" } } it "returns the value when executing the proc" do expect(subject.to_proc.call).to eq "value" end end context "with a block returning its block-level variable" do let(:block) { ->(thing) { thing } } it "returns self when executing the proc" do expect(subject.to_proc.call).to eq subject end end context "with a block referencing an attribute on the attribute" do let(:block) { -> { attribute_defined_on_attribute } } let(:result) { "other attribute value" } before do # Define an '#attribute_defined_on_attribute' instance method allowing it # be mocked. Usually this is determined via '#method_missing' missing_methods = Module.new { def attribute_defined_on_attribute(*args) end } subject.extend(missing_methods) allow(subject) .to receive(:attribute_defined_on_attribute).and_return result end it "evaluates the attribute from the attribute" do expect(subject.to_proc.call).to eq result end end context "with a block returning a sequence" do let(:block) do -> do FactoryBot::Internal.register_sequence( FactoryBot::Sequence.new(:email, 1) { |n| "foo#{n}" } ) end end it "raises a sequence abuse error" do expect { subject.to_proc.call }.to raise_error(FactoryBot::SequenceAbuseError) end end end describe FactoryBot::Attribute::Dynamic, "with a string name" do subject { FactoryBot::Attribute::Dynamic.new("name", false, -> {}) } its(:name) { should eq :name } end factory_bot-6.5.6/spec/factory_bot/attribute/sequence_spec.rb000066400000000000000000000007271507676517300245110ustar00rootroot00000000000000describe FactoryBot::Attribute::Sequence do let(:sequence_name) { :name } let(:name) { :first_name } let(:sequence) { FactoryBot::Sequence.new(sequence_name, 5) { |n| "Name #{n}" } } subject { FactoryBot::Attribute::Sequence.new(name, sequence_name, false) } before { FactoryBot::Internal.register_sequence(sequence) } its(:name) { should eq name } it "assigns the next value in the sequence" do expect(subject.to_proc.call).to eq "Name 5" end end factory_bot-6.5.6/spec/factory_bot/attribute_assignment_spec.rb000066400000000000000000000025321507676517300251250ustar00rootroot00000000000000describe "Attribute Assignment" do it "sets the default value if not overridden" do name = :user define_class("User") { attr_accessor :name, :response } factory = FactoryBot::Factory.new(name) FactoryBot::Internal.register_factory(factory) attr_1 = FactoryBot::Declaration::Dynamic.new(:name, false, -> { "orig name" }) attr_2 = FactoryBot::Declaration::Dynamic.new(:response, false, -> { "orig response" }) factory.declare_attribute(attr_1) factory.declare_attribute(attr_2) user = factory.run(FactoryBot::Strategy::Build, {}) expect(user.name).to eq "orig name" expect(user.response).to eq "orig response" end it "sets the when directly named" do name = :user define_class("User") { attr_accessor :name, :response } factory = FactoryBot::Factory.new(name) FactoryBot::Internal.register_factory(factory) attr_1 = FactoryBot::Declaration::Dynamic.new(:name, false, -> { "orig name" }) attr_2 = FactoryBot::Declaration::Dynamic.new(:response, false, -> { "orig response" }) factory.declare_attribute(attr_1) factory.declare_attribute(attr_2) user = factory.run( FactoryBot::Strategy::Build, name: "new name", response: "new response" ) expect(user.name).to eq "new name" expect(user.response).to eq "new response" end end factory_bot-6.5.6/spec/factory_bot/attribute_list_spec.rb000066400000000000000000000124351507676517300237330ustar00rootroot00000000000000module AttributeList def build_attribute_list(*attributes) FactoryBot::AttributeList.new.tap do |list| attributes.each { |attribute| list.define_attribute(attribute) } end end end describe FactoryBot::AttributeList, "#define_attribute" do it "maintains a list of attributes" do attribute = double(:attribute, name: :attribute_name) another_attribute = double(:attribute, name: :another_attribute_name) list = FactoryBot::AttributeList.new list.define_attribute(attribute) expect(list.to_a).to eq [attribute] list.define_attribute(another_attribute) expect(list.to_a).to eq [attribute, another_attribute] end it "returns the attribute" do attribute = double(:attribute, name: :attribute_name) list = FactoryBot::AttributeList.new expect(list.define_attribute(attribute)).to eq attribute end it "raises if an attribute has already been defined" do attribute = double(:attribute, name: :attribute_name) list = FactoryBot::AttributeList.new expect { 2.times { list.define_attribute(attribute) } }.to raise_error( FactoryBot::AttributeDefinitionError, "Attribute already defined: attribute_name" ) end end describe FactoryBot::AttributeList, "#define_attribute with a named attribute list" do it "raises when the attribute is a self-referencing association" do association_with_same_name = FactoryBot::Attribute::Association.new(:author, :author, {}) list = FactoryBot::AttributeList.new(:author) expect { list.define_attribute(association_with_same_name) }.to raise_error( FactoryBot::AssociationDefinitionError, "Self-referencing association 'author' in 'author'" ) end it "does not raise when the attribute is not a self-referencing association" do association_with_different_name = FactoryBot::Attribute::Association.new(:author, :post, {}) list = FactoryBot::AttributeList.new expect { list.define_attribute(association_with_different_name) }.to_not raise_error end end describe FactoryBot::AttributeList, "#apply_attributes" do include AttributeList it "adds attributes in the order defined" do attribute1 = double(:attribute1, name: :attribute1) attribute2 = double(:attribute2, name: :attribute2) list = FactoryBot::AttributeList.new list.define_attribute(attribute1) list.apply_attributes(build_attribute_list(attribute2)) expect(list.to_a).to eq [attribute1, attribute2] end end describe FactoryBot::AttributeList, "#associations" do include AttributeList it "returns associations" do email_attribute = FactoryBot::Attribute::Dynamic.new( :email, false, ->(u) { "#{u.full_name}@example.com" } ) author_attribute = FactoryBot::Attribute::Association.new(:author, :user, {}) profile_attribute = FactoryBot::Attribute::Association.new(:profile, :profile, {}) list = build_attribute_list(email_attribute, author_attribute, profile_attribute) expect(list.associations.to_a).to eq [author_attribute, profile_attribute] end end describe FactoryBot::AttributeList, "filter based on ignored attributes" do include AttributeList def build_ignored_attribute(name) FactoryBot::Attribute::Dynamic.new(name, true, -> { "value" }) end def build_non_ignored_attribute(name) FactoryBot::Attribute::Dynamic.new(name, false, -> { "value" }) end it "filters #ignored attributes" do list = build_attribute_list( build_ignored_attribute(:comments_count), build_non_ignored_attribute(:email) ) expect(list.ignored.names).to eq [:comments_count] end it "filters #non_ignored attributes" do list = build_attribute_list( build_ignored_attribute(:comments_count), build_non_ignored_attribute(:email) ) expect(list.non_ignored.names).to eq [:email] end end describe FactoryBot::AttributeList, "generating names" do include AttributeList def build_ignored_attribute(name) FactoryBot::Attribute::Dynamic.new(name, true, -> { "value" }) end def build_non_ignored_attribute(name) FactoryBot::Attribute::Dynamic.new(name, false, -> { "value" }) end def build_association(name) FactoryBot::Attribute::Association.new(name, :user, {}) end it "knows all its #names" do list = build_attribute_list( build_ignored_attribute(:comments_count), build_non_ignored_attribute(:last_name), build_association(:avatar) ) expect(list.names).to eq [:comments_count, :last_name, :avatar] end it "knows all its #names for #ignored attributes" do list = build_attribute_list( build_ignored_attribute(:posts_count), build_non_ignored_attribute(:last_name), build_association(:avatar) ) expect(list.ignored.names).to eq [:posts_count] end it "knows all its #names for #non_ignored attributes" do list = build_attribute_list( build_ignored_attribute(:posts_count), build_non_ignored_attribute(:last_name), build_association(:avatar) ) expect(list.non_ignored.names).to eq [:last_name, :avatar] end it "knows all its #names for #associations" do list = build_attribute_list( build_ignored_attribute(:posts_count), build_non_ignored_attribute(:last_name), build_association(:avatar) ) expect(list.associations.names).to eq [:avatar] end end factory_bot-6.5.6/spec/factory_bot/attribute_spec.rb000066400000000000000000000005621507676517300226760ustar00rootroot00000000000000describe FactoryBot::Attribute do it "converts the name attribute to a symbol" do name = "user" attribute = FactoryBot::Attribute.new(name, false) expect(attribute.name).to eq name.to_sym end it "is not an association" do name = "user" attribute = FactoryBot::Attribute.new(name, false) expect(attribute).not_to be_association end end factory_bot-6.5.6/spec/factory_bot/callback_spec.rb000066400000000000000000000015411507676517300224250ustar00rootroot00000000000000describe FactoryBot::Callback do it "has a name" do expect(FactoryBot::Callback.new(:after_create, -> {}).name).to eq :after_create end it "converts strings to symbols" do expect(FactoryBot::Callback.new("after_create", -> {}).name).to eq :after_create end it "runs its block with no parameters" do ran_with = nil FactoryBot::Callback.new(:after_create, -> { ran_with = [] }).run(:one, :two) expect(ran_with).to eq [] end it "runs its block with one parameter" do ran_with = nil FactoryBot::Callback.new(:after_create, ->(one) { ran_with = [one] }).run(:one, :two) expect(ran_with).to eq [:one] end it "runs its block with two parameters" do ran_with = nil FactoryBot::Callback.new(:after_create, ->(one, two) { ran_with = [one, two] }).run(:one, :two) expect(ran_with).to eq [:one, :two] end end factory_bot-6.5.6/spec/factory_bot/declaration/000077500000000000000000000000001507676517300216165ustar00rootroot00000000000000factory_bot-6.5.6/spec/factory_bot/declaration/association_spec.rb000066400000000000000000000022661507676517300254770ustar00rootroot00000000000000describe FactoryBot::Declaration::Association do describe "#==" do context "when the attributes are equal" do it "the objects are equal" do declaration = described_class.new(:name, options: true) other_declaration = described_class.new(:name, options: true) expect(declaration).to eq(other_declaration) end end context "when the names are different" do it "the objects are NOT equal" do declaration = described_class.new(:name, options: true) other_declaration = described_class.new(:other_name, options: true) expect(declaration).not_to eq(other_declaration) end end context "when the options are different" do it "the objects are NOT equal" do declaration = described_class.new(:name, options: true) other_declaration = described_class.new(:name, other_options: true) expect(declaration).not_to eq(other_declaration) end end context "when comparing against another type of object" do it "the objects are NOT equal" do declaration = described_class.new(:name) expect(declaration).not_to eq(:not_a_declaration) end end end end factory_bot-6.5.6/spec/factory_bot/declaration/dynamic_spec.rb000066400000000000000000000030461507676517300246040ustar00rootroot00000000000000describe FactoryBot::Declaration::Dynamic do describe "#==" do context "when the attributes are equal" do it "the objects are equal" do block = -> {} declaration = described_class.new(:name, false, block) other_declaration = described_class.new(:name, false, block) expect(declaration).to eq(other_declaration) end end context "when the names are different" do it "the objects are NOT equal" do block = -> {} declaration = described_class.new(:name, false, block) other_declaration = described_class.new(:other_name, false, block) expect(declaration).not_to eq(other_declaration) end end context "when the blocks are different" do it "the objects are NOT equal" do declaration = described_class.new(:name, false, -> {}) other_declaration = described_class.new(:name, false, -> {}) expect(declaration).not_to eq(other_declaration) end end context "when one is ignored and the other isn't" do it "the objects are NOT equal" do block = -> {} declaration = described_class.new(:name, false, block) other_declaration = described_class.new(:name, true, block) expect(declaration).not_to eq(other_declaration) end end context "when comparing against another type of object" do it "the objects are NOT equal" do declaration = described_class.new(:name, false, -> {}) expect(declaration).not_to eq(:not_a_declaration) end end end end factory_bot-6.5.6/spec/factory_bot/declaration/implicit_spec.rb000066400000000000000000000054511507676517300247740ustar00rootroot00000000000000describe FactoryBot::Declaration::Implicit do context "with a known factory" do it "creates an association attribute" do allow(FactoryBot.factories).to receive(:registered?).and_return true declaration = FactoryBot::Declaration::Implicit.new(:name) attribute = declaration.to_attributes.first expect(attribute).to be_association end it "has the correct factory name" do allow(FactoryBot.factories).to receive(:registered?).and_return true name = :factory_name declaration = FactoryBot::Declaration::Implicit.new(name) attribute = declaration.to_attributes.first expect(attribute.factory).to eq(name) end end context "with a known sequence" do it "does not create an association attribute" do allow(FactoryBot::Internal.sequences).to receive(:registered?).and_return true declaration = FactoryBot::Declaration::Implicit.new(:name) attribute = declaration.to_attributes.first expect(attribute).not_to be_association end it "creates a sequence attribute" do allow(FactoryBot::Internal.sequences).to receive(:registered?).and_return true declaration = FactoryBot::Declaration::Implicit.new(:name) attribute = declaration.to_attributes.first expect(attribute).to be_a(FactoryBot::Attribute::Sequence) end end describe "#==" do context "when the attributes are equal" do it "the objects are equal" do declaration = described_class.new(:name, :factory, false) other_declaration = described_class.new(:name, :factory, false) expect(declaration).to eq(other_declaration) end end context "when the names are different" do it "the objects are NOT equal" do declaration = described_class.new(:name, :factory, false) other_declaration = described_class.new(:other_name, :factory, false) expect(declaration).not_to eq(other_declaration) end end context "when the factories are different" do it "the objects are NOT equal" do declaration = described_class.new(:name, :factory, false) other_declaration = described_class.new(:name, :other_factory, false) expect(declaration).not_to eq(other_declaration) end end context "when one is ignored and the other isn't" do it "the objects are NOT equal" do declaration = described_class.new(:name, :factory, false) other_declaration = described_class.new(:name, :factory, true) expect(declaration).not_to eq(other_declaration) end end context "when comparing against another type of object" do it "the objects are NOT equal" do declaration = described_class.new(:name, :factory, false) expect(declaration).not_to eq(:not_a_declaration) end end end end factory_bot-6.5.6/spec/factory_bot/declaration_list_spec.rb000066400000000000000000000073021507676517300242120ustar00rootroot00000000000000describe FactoryBot::DeclarationList, "#attributes" do it "returns an AttributeList" do declaration_list = FactoryBot::DeclarationList.new expect(declaration_list.attributes).to be_a(FactoryBot::AttributeList) end it "defines each attribute on the attribute list" do attribute1 = double("attribute 1") attribute2 = double("attribute 2") attribute3 = double("attribute 3") declaration1 = double("declaration 1", to_attributes: [attribute1, attribute2]) declaration2 = double("declaration2", to_attributes: [attribute3]) attribute_list = double("attribute list", define_attribute: true) declaration_list = FactoryBot::DeclarationList.new allow(FactoryBot::AttributeList).to receive(:new).and_return attribute_list declaration_list.declare_attribute(declaration1) declaration_list.declare_attribute(declaration2) declaration_list.attributes expect(attribute_list).to have_received(:define_attribute).with(attribute1) expect(attribute_list).to have_received(:define_attribute).with(attribute2) expect(attribute_list).to have_received(:define_attribute).with(attribute3) end end describe FactoryBot::DeclarationList, "#declare_attribute" do it "adds the declaration to the list when not overridable" do declaration1 = double("declaration", name: "declaration 1") declaration2 = double("declaration", name: "declaration 2") declaration_list = FactoryBot::DeclarationList.new declaration_list.declare_attribute(declaration1) expect(declaration_list.to_a).to eq [declaration1] declaration_list.declare_attribute(declaration2) expect(declaration_list.to_a).to eq [declaration1, declaration2] end it "adds the declaration to the list when overridable" do declaration1 = double("declaration", name: "declaration 1") declaration2 = double("declaration", name: "declaration 2") declaration_list = FactoryBot::DeclarationList.new declaration_list.overridable declaration_list.declare_attribute(declaration1) expect(declaration_list.to_a).to eq [declaration1] declaration_list.declare_attribute(declaration2) expect(declaration_list.to_a).to eq [declaration1, declaration2] end it "deletes declarations with the same name when overridable" do declaration1 = double("declaration", name: "declaration 1") declaration2 = double("declaration", name: "declaration 2") declaration_with_same_name = double("declaration", name: "declaration 1") declaration_list = FactoryBot::DeclarationList.new declaration_list.overridable declaration_list.declare_attribute(declaration1) expect(declaration_list.to_a).to eq [declaration1] declaration_list.declare_attribute(declaration2) expect(declaration_list.to_a).to eq [declaration1, declaration2] declaration_list.declare_attribute(declaration_with_same_name) expect(declaration_list.to_a).to eq [declaration2, declaration_with_same_name] end it "appends declarations with the same name when NOT overridable" do declaration1 = double("declaration", name: "declaration 1") declaration2 = double("declaration", name: "declaration 2") declaration_with_same_name = double("declaration", name: "declaration 1") # DeclarationList's `@overridable` attr is set to false by default declaration_list = FactoryBot::DeclarationList.new declaration_list.declare_attribute(declaration1) expect(declaration_list.to_a).to eq [declaration1] declaration_list.declare_attribute(declaration2) expect(declaration_list.to_a).to eq [declaration1, declaration2] declaration_list.declare_attribute(declaration_with_same_name) expect(declaration_list.to_a).to eq [declaration1, declaration2, declaration_with_same_name] end end factory_bot-6.5.6/spec/factory_bot/decorator/000077500000000000000000000000001507676517300213135ustar00rootroot00000000000000factory_bot-6.5.6/spec/factory_bot/decorator/attribute_hash_spec.rb000066400000000000000000000013161507676517300256610ustar00rootroot00000000000000describe FactoryBot::Decorator::AttributeHash do describe "#attributes" do it "returns a hash of attributes" do attributes = {attribute_1: :value, attribute_2: :value} component = double(:component, attributes) decorator = described_class.new(component, [:attribute_1, :attribute_2]) expect(decorator.attributes).to eq(attributes) end context "with an attribute called 'attributes'" do it "does not call itself recursively" do attributes = {attributes: :value} component = double(:component, attributes) decorator = described_class.new(component, [:attributes]) expect(decorator.attributes).to eq(attributes) end end end end factory_bot-6.5.6/spec/factory_bot/definition_proxy_spec.rb000066400000000000000000000234011507676517300242610ustar00rootroot00000000000000describe FactoryBot::DefinitionProxy, "#add_attribute" do it "declares a dynamic attribute on the factory when the proxy respects attributes" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) attribute_value = -> { "dynamic attribute" } proxy.add_attribute(:attribute_name, &attribute_value) expect(definition).to have_dynamic_declaration(:attribute_name) .with_value(attribute_value) end it "declares a dynamic attribute on the factory when the proxy ignores attributes" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition, true) attribute_value = -> { "dynamic attribute" } proxy.add_attribute(:attribute_name, &attribute_value) expect(definition).to have_dynamic_declaration(:attribute_name) .ignored .with_value(attribute_value) end end describe FactoryBot::DefinitionProxy, "#transient" do it "makes all attributes added ignored" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) attribute_value = -> { "dynamic_attribute" } proxy.transient do add_attribute(:attribute_name, &attribute_value) end expect(definition).to have_dynamic_declaration(:attribute_name) .ignored .with_value(attribute_value) end end describe FactoryBot::DefinitionProxy, "#method_missing" do it "declares an implicit declaration when called without args or a block" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) proxy.bogus expect(definition).to have_implicit_declaration(:bogus).with_factory(definition) end it "declares an association when called with a ':factory' key" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) proxy.author factory: :user expect(definition).to have_association_declaration(:author) .with_options(factory: :user) end it "declares a dynamic attribute when called with a block" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) attribute_value = -> { "dynamic attribute" } proxy.attribute_name(&attribute_value) expect(definition).to have_dynamic_declaration(:attribute_name) .with_value(attribute_value) end it "raises a NoMethodError when called with a static-attribute-like argument" do definition = FactoryBot::Definition.new(:broken) proxy = FactoryBot::DefinitionProxy.new(definition) invalid_call = -> { proxy.static_attributes_are_gone "true" } expect(&invalid_call).to raise_error( NoMethodError, /'static_attributes_are_gone'.*'broken' factory.*Did you mean\? 'static_attributes_are_gone \{ "true" \}'/m ) end end describe FactoryBot::DefinitionProxy, "#sequence" do def build_proxy(factory_name) definition = FactoryBot::Definition.new(factory_name) FactoryBot::DefinitionProxy.new(definition) end it "creates a new sequence starting at 1" do allow(FactoryBot::Sequence).to receive(:new).and_call_original proxy = build_proxy(:factory) proxy.sequence(:sequence) expect(FactoryBot::Sequence).to have_received(:new).with(:sequence, {uri_paths: []}) end it "creates a new sequence with an overridden starting value" do allow(FactoryBot::Sequence).to receive(:new).and_call_original proxy = build_proxy(:factory) override = "override" proxy.sequence(:sequence, override) expect(FactoryBot::Sequence).to have_received(:new) .with(:sequence, override, {uri_paths: []}) end it "creates a new sequence with a block" do allow(FactoryBot::Sequence).to receive(:new).and_call_original sequence_block = proc { |n| "user+#{n}@example.com" } proxy = build_proxy(:factory) proxy.sequence(:sequence, 1, &sequence_block) expect(FactoryBot::Sequence).to have_received(:new) .with(:sequence, 1, {uri_paths: []}, &sequence_block) end end describe FactoryBot::DefinitionProxy, "#association" do it "declares an association" do definition = FactoryBot::Definition.new(:definition_name) proxy = FactoryBot::DefinitionProxy.new(definition) proxy.association(:association_name) expect(definition).to have_association_declaration(:association_name) end it "declares an association with options" do definition = FactoryBot::Definition.new(:definition_name) proxy = FactoryBot::DefinitionProxy.new(definition) proxy.association(:association_name, name: "Awesome") expect(definition).to have_association_declaration(:association_name) .with_options(name: "Awesome") end it "when passing a block raises an error" do definition = FactoryBot::Definition.new(:post) proxy = FactoryBot::DefinitionProxy.new(definition) expect { proxy.association(:author) {} } .to raise_error( FactoryBot::AssociationDefinitionError, "Unexpected block passed to 'author' association in 'post' factory" ) end end describe FactoryBot::DefinitionProxy, "adding callbacks" do it "adding a :before_all callback succeeds" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) callback = -> { "my awesome callback!" } proxy.before(:all, &callback) expect(definition).to have_callback(:before_all).with_block(callback) end it "adding an :after_all callback succeeds" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) callback = -> { "my awesome callback!" } proxy.after(:all, &callback) expect(definition).to have_callback(:after_all).with_block(callback) end it "adding an :after_build callback succeeds" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) callback = -> { "my awesome callback!" } proxy.after(:build, &callback) expect(definition).to have_callback(:after_build).with_block(callback) end it "adding an :after_create callback succeeds" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) callback = -> { "my awesome callback!" } proxy.after(:create, &callback) expect(definition).to have_callback(:after_create).with_block(callback) end it "adding an :after_stub callback succeeds" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) callback = -> { "my awesome callback!" } proxy.after(:stub, &callback) expect(definition).to have_callback(:after_stub).with_block(callback) end it "adding both an :after_stub and an :after_create callback succeeds" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) callback = -> { "my awesome callback!" } proxy.after(:stub, :create, &callback) expect(definition).to have_callback(:after_stub).with_block(callback) expect(definition).to have_callback(:after_create).with_block(callback) end it "adding both a :before_stub and a :before_create callback succeeds" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) callback = -> { "my awesome callback!" } proxy.before(:stub, :create, &callback) expect(definition).to have_callback(:before_stub).with_block(callback) expect(definition).to have_callback(:before_create).with_block(callback) end it "adding both an :after_stub and a :before_create callback succeeds" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) callback = -> { "my awesome callback!" } proxy.callback(:after_stub, :before_create, &callback) expect(definition).to have_callback(:after_stub).with_block(callback) expect(definition).to have_callback(:before_create).with_block(callback) end end describe FactoryBot::DefinitionProxy, "#to_create" do it "accepts a block to run in place of #save!" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) to_create_block = ->(record) { record.persist } proxy.to_create(&to_create_block) expect(definition.to_create).to eq to_create_block end end describe FactoryBot::DefinitionProxy, "#factory" do it "without options" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) proxy.factory(:child) expect(proxy.child_factories).to include([:child, {}, nil]) end it "with options" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) proxy.factory(:child, awesome: true) expect(proxy.child_factories).to include([:child, {awesome: true}, nil]) end it "with a block" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) child_block = -> {} proxy.factory(:child, {}, &child_block) expect(proxy.child_factories).to include([:child, {}, child_block]) end end describe FactoryBot::DefinitionProxy, "#trait" do it "declares a trait" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) male_trait = proc { gender { "Male" } } proxy.trait(:male, &male_trait) expect(definition).to have_trait(:male).with_block(male_trait) end end describe FactoryBot::DefinitionProxy, "#initialize_with" do it "defines the constructor on the definition" do definition = FactoryBot::Definition.new(:name) proxy = FactoryBot::DefinitionProxy.new(definition) constructor = proc { [] } proxy.initialize_with(&constructor) expect(definition.constructor).to eq constructor end end factory_bot-6.5.6/spec/factory_bot/definition_spec.rb000066400000000000000000000045541507676517300230300ustar00rootroot00000000000000describe FactoryBot::Definition do it "delegates :declare_attribute to declarations" do definition = described_class.new(:name) expect(definition).to delegate(:declare_attribute).to(:declarations) end it "creates a new attribute list with the name passed when given a name" do name = "great name" allow(FactoryBot::DeclarationList).to receive(:new) FactoryBot::Definition.new(name) expect(FactoryBot::DeclarationList).to have_received(:new).with(name) end it "has a name" do name = "factory name" definition = described_class.new(name) expect(definition.name).to eq(name) end it "has an overridable declaration list" do list = double("declaration list", overridable: true) allow(FactoryBot::DeclarationList).to receive(:new).and_return list definition = described_class.new(:name) expect(definition.overridable).to eq definition expect(list).to have_received(:overridable).once end it "maintains a list of traits" do trait1 = double(:trait) trait2 = double(:trait) definition = described_class.new(:name) definition.define_trait(trait1) definition.define_trait(trait2) expect(definition.defined_traits).to include(trait1, trait2) end it "adds only unique traits" do trait1 = double(:trait) definition = described_class.new(:name) definition.define_trait(trait1) definition.define_trait(trait1) expect(definition.defined_traits.size).to eq 1 end it "maintains a list of callbacks" do callback1 = "callback1" callback2 = "callback2" definition = described_class.new(:name) definition.add_callback(callback1) definition.add_callback(callback2) expect(definition.callbacks).to eq [callback1, callback2] end it "doesn't expose a separate create strategy when none is specified" do definition = described_class.new(:name) expect(definition.to_create).to be_nil end it "exposes a non-default create strategy when one is provided by the user" do definition = described_class.new(:name) block = proc {} definition.to_create(&block) expect(definition.to_create).to eq block end it "maintains a list of enum fields" do definition = described_class.new(:name) enum_field = double("enum_field") definition.register_enum(enum_field) expect(definition.registered_enums).to include(enum_field) end end factory_bot-6.5.6/spec/factory_bot/disallows_duplicates_registry_spec.rb000066400000000000000000000016221507676517300270370ustar00rootroot00000000000000describe FactoryBot::Decorator::DisallowsDuplicatesRegistry do it "delegates #register to the registry when not registered" do registry = double("registry", name: "Great thing", register: true) decorator = FactoryBot::Decorator::DisallowsDuplicatesRegistry.new(registry) allow(registry).to receive(:registered?).and_return false decorator.register(:awesome, {}) expect(registry).to have_received(:register).with(:awesome, {}) end it "raises when attempting to #register a previously registered strategy" do registry = double("registry", name: "Great thing", register: true) decorator = FactoryBot::Decorator::DisallowsDuplicatesRegistry.new(registry) allow(registry).to receive(:registered?).and_return true expect { decorator.register(:same_name, {}) } .to raise_error(FactoryBot::DuplicateDefinitionError, "Great thing already registered: same_name") end end factory_bot-6.5.6/spec/factory_bot/evaluator_class_definer_spec.rb000066400000000000000000000045301507676517300255550ustar00rootroot00000000000000describe FactoryBot::EvaluatorClassDefiner do it "returns an evaluator when accessing the evaluator class" do evaluator = define_evaluator(parent_class: FactoryBot::Evaluator) expect(evaluator).to be_a(FactoryBot::Evaluator) end it "adds each attribute to the evaluator" do attribute = stub_attribute(:attribute) { 1 } evaluator = define_evaluator(attributes: [attribute]) expect(evaluator.attribute).to eq 1 end it "evaluates the block in the context of the evaluator" do dependency_attribute = stub_attribute(:dependency) { 1 } attribute = stub_attribute(:attribute) { dependency + 1 } evaluator = define_evaluator(attributes: [dependency_attribute, attribute]) expect(evaluator.attribute).to eq 2 end it "only instance_execs the block once even when returning nil" do count = 0 attribute = stub_attribute(:attribute) { count += 1 nil } evaluator = define_evaluator(attributes: [attribute]) 2.times { evaluator.attribute } expect(count).to eq 1 end it "sets attributes on the evaluator class" do attributes = [stub_attribute, stub_attribute] evaluator = define_evaluator(attributes: attributes) expect(evaluator.attribute_lists).to eq [attributes] end context "with a custom evaluator as a parent class" do it "bases its attribute lists on itself and its parent evaluator" do parent_attributes = [stub_attribute, stub_attribute] parent_evaluator_class = define_evaluator_class(attributes: parent_attributes) child_attributes = [stub_attribute, stub_attribute] child_evaluator = define_evaluator( attributes: child_attributes, parent_class: parent_evaluator_class ) expect(child_evaluator.attribute_lists).to eq [parent_attributes, child_attributes] end end def define_evaluator(arguments = {}) evaluator_class = define_evaluator_class(arguments) evaluator_class.new(FactoryBot::Strategy::Null) end def define_evaluator_class(arguments = {}) evaluator_class_definer = FactoryBot::EvaluatorClassDefiner.new( arguments[:attributes] || [], arguments[:parent_class] || FactoryBot::Evaluator ) evaluator_class_definer.evaluator_class end def stub_attribute(name = :attribute, &value) value ||= -> {} double(name.to_s, name: name.to_sym, to_proc: value) end end factory_bot-6.5.6/spec/factory_bot/factory_spec.rb000066400000000000000000000244151507676517300223450ustar00rootroot00000000000000describe FactoryBot::Factory do it "has a factory name" do name = :user factory = FactoryBot::Factory.new(name) FactoryBot::Internal.register_factory(factory) expect(factory.name).to eq name end it "has a build class" do name = :user klass = define_class("User") factory = FactoryBot::Factory.new(name) FactoryBot::Internal.register_factory(factory) expect(factory.build_class).to eq klass end it "returns associations" do define_class("Post") factory = FactoryBot::Factory.new(:post) FactoryBot::Internal.register_factory(FactoryBot::Factory.new(:admin)) factory.declare_attribute(FactoryBot::Declaration::Association.new(:author, {})) factory.declare_attribute(FactoryBot::Declaration::Association.new(:editor, {})) factory.declare_attribute(FactoryBot::Declaration::Implicit.new(:admin, factory)) factory.associations.each do |association| expect(association).to be_association end expect(factory.associations.to_a.length).to eq 3 end it "includes associations from the parent factory" do association_on_parent = FactoryBot::Declaration::Association.new(:association_on_parent, {}) association_on_child = FactoryBot::Declaration::Association.new(:association_on_child, {}) define_class("Post") factory = FactoryBot::Factory.new(:post) factory.declare_attribute(association_on_parent) FactoryBot::Internal.register_factory(factory) child_factory = FactoryBot::Factory.new(:child_post, parent: :post) child_factory.declare_attribute(association_on_child) expect(child_factory.associations.map(&:name)).to eq [:association_on_parent, :association_on_child] end describe "when overriding generated attributes with a hash" do it "returns the overridden value in the generated attributes" do name = :name value = "The price is right!" hash = {name => value} define_class("Name") factory = FactoryBot::Factory.new(name) declaration = FactoryBot::Declaration::Dynamic.new(name, false, -> { flunk }) factory.declare_attribute(declaration) result = factory.run(FactoryBot::Strategy::AttributesFor, hash) expect(result[name]).to eq value end it "overrides a symbol parameter with a string parameter" do name = :name define_class("Name") value = "The price is right!" factory = FactoryBot::Factory.new(name) FactoryBot::Internal.register_factory(factory) declaration = FactoryBot::Declaration::Dynamic.new(name, false, -> { flunk }) factory.declare_attribute(declaration) hash = {name.to_s => value} result = factory.run(FactoryBot::Strategy::AttributesFor, hash) expect(result[name]).to eq value end end describe "overriding an attribute with an alias" do it "uses the passed in value for the alias" do name = :user define_class("User") factory = FactoryBot::Factory.new(name) FactoryBot::Internal.register_factory(factory) attribute = FactoryBot::Declaration::Dynamic.new( :test, false, -> { "original" } ) factory.declare_attribute(attribute) FactoryBot.aliases << [/(.*)_alias/, '\1'] result = factory.run( FactoryBot::Strategy::AttributesFor, test_alias: "new" ) expect(result[:test_alias]).to eq "new" end it "discards the predefined value for the attribute" do name = :user define_class("User") factory = FactoryBot::Factory.new(name) FactoryBot::Internal.register_factory(factory) attribute = FactoryBot::Declaration::Dynamic.new( :test, false, -> { "original" } ) factory.declare_attribute(attribute) FactoryBot.aliases << [/(.*)_alias/, '\1'] result = factory.run( FactoryBot::Strategy::AttributesFor, test_alias: "new" ) expect(result[:test]).to be_nil end end it "guesses the build class from the factory name" do name = :user define_class("User") factory = FactoryBot::Factory.new(name) FactoryBot::Internal.register_factory(factory) expect(factory.build_class).to eq User end it "creates a new factory using the class of the parent" do name = :user define_class("User") factory = FactoryBot::Factory.new(name) FactoryBot::Internal.register_factory(factory) child = FactoryBot::Factory.new(:child, parent: factory.name) child.compile expect(child.build_class).to eq factory.build_class end it "creates a new factory while overriding the parent class" do name = :user define_class("User") factory = FactoryBot::Factory.new(name) FactoryBot::Internal.register_factory(factory) child = FactoryBot::Factory.new(:child, class: String, parent: factory.name) child.compile expect(child.build_class).to eq String end end describe FactoryBot::Factory, "when defined with a custom class" do it "is an instance of that custom class" do factory = FactoryBot::Factory.new(:author, class: Float) expect(factory.build_class).to eq Float end end describe FactoryBot::Factory, "when given a class that overrides #to_s" do it "sets build_class correctly" do define_class("Overriding") define_class("Overriding::Class") do def self.to_s "Overriding" end end overriding_class = Overriding::Class factory = FactoryBot::Factory.new(:overriding_class, class: Overriding::Class) expect(factory.build_class).to eq overriding_class end end describe FactoryBot::Factory, "when defined with a class instead of a name" do it "has a name" do klass = ArgumentError name = :argument_error factory = FactoryBot::Factory.new(klass) expect(factory.name).to eq name end it "has a build_class" do klass = ArgumentError factory = FactoryBot::Factory.new(klass) expect(factory.build_class).to eq klass end end describe FactoryBot::Factory, "when defined with a custom class name" do it "has a build_class equal to its custom class name" do factory = FactoryBot::Factory.new(:author, class: :argument_error) expect(factory.build_class).to eq ArgumentError end end describe FactoryBot::Factory, "with a name ending in s" do it "has a name" do factory = FactoryBot::Factory.new(:business) expect(factory.name).to eq :business end it "has a build class" do define_class("Business") factory = FactoryBot::Factory.new(:business) expect(factory.build_class).to eq Business end end describe FactoryBot::Factory, "with a string for a name" do it "has a name" do name = :string factory = FactoryBot::Factory.new(name.to_s) expect(factory.name).to eq name end it "sets build_class correctly with a class with an underscore" do name = :settings define_class("Admin_Settings_1") settings_class = Admin_Settings_1 factory = FactoryBot::Factory.new(name, class: "Admin_Settings_1") expect(factory.build_class).to eq settings_class end end describe FactoryBot::Factory, "for namespaced class" do it "sets build_class correctly with a namespaced class with Namespace::Class syntax" do name = :settings define_class("Admin") define_class("Admin::Settings") settings_class = Admin::Settings factory = FactoryBot::Factory.new(name, class: "Admin::Settings") expect(factory.build_class).to eq settings_class end it "sets build_class correctly with a namespaced class with namespace/class syntax" do name = :settings define_class("Admin") define_class("Admin::Settings") settings_class = Admin::Settings factory = FactoryBot::Factory.new(name, class: "admin/settings") expect(factory.build_class).to eq settings_class end end describe FactoryBot::Factory, "human names" do it "parses names without underscores" do factory = FactoryBot::Factory.new(:user) expect(factory.names).to eq [:user] end it "parses human names without underscores" do factory = FactoryBot::Factory.new(:user) expect(factory.human_names).to eq ["user"] end it "parses names with underscores" do factory = FactoryBot::Factory.new(:happy_user) expect(factory.names).to eq [:happy_user] end it "parses human names with underscores" do factory = FactoryBot::Factory.new(:happy_user) expect(factory.human_names).to eq ["happy user"] end it "parses names with big letters" do factory = FactoryBot::Factory.new(:LoL) expect(factory.names).to eq [:LoL] end it "parses human names with big letters" do factory = FactoryBot::Factory.new(:LoL) expect(factory.human_names).to eq ["lol"] end it "parses names with aliases" do factory = FactoryBot::Factory.new(:happy_user, aliases: [:gleeful_user, :person]) expect(factory.names).to eq [:happy_user, :gleeful_user, :person] end it "parses human names with aliases" do factory = FactoryBot::Factory.new(:happy_user, aliases: [:gleeful_user, :person]) expect(factory.human_names).to eq ["happy user", "gleeful user", "person"] end end describe FactoryBot::Factory, "running a factory" do def build_factory attribute = FactoryBot::Attribute::Dynamic.new(:name, false, -> { "value" }) attributes = [attribute] declaration = FactoryBot::Declaration::Dynamic.new(:name, false, -> { "value" }) strategy = double("strategy", result: "result") define_model("User", name: :string) allow(FactoryBot::Declaration::Dynamic).to receive(:new) .and_return declaration allow(declaration).to receive(:to_attributes).and_return attributes allow(FactoryBot::Strategy::Build).to receive(:new).and_return strategy factory = FactoryBot::Factory.new(:user) factory.declare_attribute(declaration) factory end it "creates the right strategy using the build class when running" do factory = build_factory factory.run(FactoryBot::Strategy::Build, {}) expect(FactoryBot::Strategy::Build).to have_received(:new).once end it "returns the result from the strategy when running" do factory = build_factory expect(factory.run(FactoryBot::Strategy::Build, {})).to eq "result" end it "calls the block and returns the result" do factory = build_factory block_run = nil block = ->(_result) { block_run = "changed" } factory.run(FactoryBot::Strategy::Build, {}, &block) expect(block_run).to eq "changed" end end factory_bot-6.5.6/spec/factory_bot/find_definitions_spec.rb000066400000000000000000000074041507676517300242100ustar00rootroot00000000000000require "fileutils" shared_examples_for "finds definitions" do before do allow(FactoryBot).to receive(:load) FactoryBot.find_definitions end subject { FactoryBot } end RSpec::Matchers.define :load_definitions_from do |file| match do |given| @has_received = have_received(:load).with(File.expand_path(file)) @has_received.matches?(given) end description do "load definitions from #{file}" end failure_message do @has_received.failure_message end end describe "definition loading" do def self.in_directory_with_files(*files) before do @pwd = Dir.pwd @tmp_dir = File.join(File.dirname(__FILE__), "tmp") FileUtils.mkdir_p @tmp_dir Dir.chdir(@tmp_dir) files.each do |file| FileUtils.mkdir_p File.dirname(file) FileUtils.touch file end end after do Dir.chdir(@pwd) FileUtils.rm_rf(@tmp_dir) end end describe "with factories.rb" do in_directory_with_files "factories.rb" it_should_behave_like "finds definitions" do it { should load_definitions_from("factories.rb") } end end %w[spec test].each do |dir| describe "with a factories file under #{dir}" do in_directory_with_files File.join(dir, "factories.rb") it_should_behave_like "finds definitions" do it { should load_definitions_from("#{dir}/factories.rb") } end end describe "with a factories file under #{dir}/factories" do in_directory_with_files File.join(dir, "factories", "post_factory.rb") it_should_behave_like "finds definitions" do it { should load_definitions_from("#{dir}/factories/post_factory.rb") } end end describe "with several factories files under #{dir}/factories" do in_directory_with_files File.join(dir, "factories", "post_factory.rb"), File.join(dir, "factories", "person_factory.rb") it_should_behave_like "finds definitions" do it { should load_definitions_from("#{dir}/factories/post_factory.rb") } it { should load_definitions_from("#{dir}/factories/person_factory.rb") } end end describe "with several factories files under #{dir}/factories in non-alphabetical order" do in_directory_with_files File.join(dir, "factories", "b.rb"), File.join(dir, "factories", "a.rb") it "loads the files in the right order" do allow(FactoryBot).to receive(:load) wd = File.dirname(__FILE__) file_b = File.join(wd, "tmp", dir, "factories", "b.rb") file_a = File.join(wd, "tmp", dir, "factories", "a.rb") FactoryBot.find_definitions expect(FactoryBot).to have_received(:load).with(file_a).ordered expect(FactoryBot).to have_received(:load).with(file_b).ordered end end describe "with nested and unnested factories files under #{dir}" do in_directory_with_files File.join(dir, "factories.rb"), File.join(dir, "factories", "post_factory.rb"), File.join(dir, "factories", "person_factory.rb") it_should_behave_like "finds definitions" do it { should load_definitions_from("#{dir}/factories.rb") } it { should load_definitions_from("#{dir}/factories/post_factory.rb") } it { should load_definitions_from("#{dir}/factories/person_factory.rb") } end end describe "with deeply nested factory files under #{dir}" do in_directory_with_files File.join(dir, "factories", "subdirectory", "post_factory.rb"), File.join(dir, "factories", "subdirectory", "person_factory.rb") it_should_behave_like "finds definitions" do it { should load_definitions_from("#{dir}/factories/subdirectory/post_factory.rb") } it { should load_definitions_from("#{dir}/factories/subdirectory/person_factory.rb") } end end end end factory_bot-6.5.6/spec/factory_bot/internal_spec.rb000066400000000000000000000111121507676517300225000ustar00rootroot00000000000000describe FactoryBot::Internal do describe ".register_trait" do it "registers the provided trait" do trait = FactoryBot::Trait.new(:admin) configuration = FactoryBot::Internal.configuration expect { FactoryBot::Internal.register_trait(trait) } .to change { configuration.traits.count } .from(0) .to(1) end it "returns the registered trait" do trait = FactoryBot::Trait.new(:admin) expect(FactoryBot::Internal.register_trait(trait)).to eq trait end end describe ".trait_by_name" do it "finds a previously registered trait" do trait = FactoryBot::Trait.new(:admin) klass = instance_double("klass") FactoryBot::Internal.register_trait(trait) expect(trait.klass).to be_nil expect(FactoryBot::Internal.trait_by_name(trait.name, klass)).to eq trait expect(trait.klass).to eq klass end end describe ".register_sequence" do it "registers the provided sequence" do sequence = FactoryBot::Sequence.new(:email) configuration = FactoryBot::Internal.configuration expect { FactoryBot::Internal.register_sequence(sequence) } .to change { configuration.sequences.count } .from(0) .to(1) end it "returns the registered sequence" do sequence = FactoryBot::Sequence.new(:email) expect(FactoryBot::Internal.register_sequence(sequence)).to eq sequence end end describe ".sequence_by_name" do it "finds a registered sequence" do sequence = FactoryBot::Sequence.new(:email) FactoryBot::Internal.register_sequence(sequence) expect(FactoryBot::Internal.sequence_by_name(sequence.name)).to eq sequence end end describe ".rewind_sequences" do it "rewinds the sequences and the internal sequences" do sequence = instance_double(FactoryBot::Sequence, names: ["email"]) allow(sequence).to receive(:rewind) FactoryBot::Internal.register_sequence(sequence) inline_sequence = instance_double(FactoryBot::Sequence) allow(inline_sequence).to receive(:rewind) FactoryBot::Internal.register_inline_sequence(inline_sequence) FactoryBot::Internal.rewind_sequences expect(sequence).to have_received(:rewind).exactly(:once) expect(inline_sequence).to have_received(:rewind).exactly(:once) end end describe ".register_factory" do it "registers the provided factory" do factory = FactoryBot::Factory.new(:object) configuration = FactoryBot::Internal.configuration expect { FactoryBot::Internal.register_factory(factory) } .to change { configuration.factories.count } .from(0) .to(1) end it "returns the registered factory" do factory = FactoryBot::Factory.new(:object) expect(FactoryBot::Internal.register_factory(factory)).to eq factory end end describe ".factory_by_name" do it "finds a registered factory" do factory = FactoryBot::Factory.new(:object) FactoryBot::Internal.register_factory(factory) expect(FactoryBot::Internal.factory_by_name(factory.name)).to eq factory end end describe ".register_factory" do it "registers the provided factory" do factory = FactoryBot::Factory.new(:object) configuration = FactoryBot::Internal.configuration expect { FactoryBot::Internal.register_factory(factory) } .to change { configuration.factories.count } .from(0) .to(1) end it "returns the registered factory" do factory = FactoryBot::Factory.new(:object) expect(FactoryBot::Internal.register_factory(factory)).to eq factory end end describe ".factory_by_name" do it "finds a registered factory" do factory = FactoryBot::Factory.new(:object) FactoryBot::Internal.register_factory(factory) expect(FactoryBot::Internal.factory_by_name(factory.name)).to eq factory end end describe ".register_strategy" do it "register the provided strategy name with the class" do configuration = FactoryBot::Internal.configuration initial_strategies_count = configuration.strategies.count expect { FactoryBot::Internal.register_strategy(:strategy_name, :strategy_class) }.to change { configuration.strategies.count } .from(initial_strategies_count) .to(initial_strategies_count + 1) end end describe ".strategy_by_name" do it "finds a registered strategy" do FactoryBot::Internal.register_strategy(:strategy_name, :strategy_class) expect(FactoryBot::Internal.strategy_by_name(:strategy_name)) .to eq :strategy_class end end end factory_bot-6.5.6/spec/factory_bot/null_factory_spec.rb000066400000000000000000000026371507676517300234010ustar00rootroot00000000000000describe FactoryBot::NullFactory do it "delegates defined traits to its definition" do null_factory = FactoryBot::NullFactory.new expect(null_factory).to delegate(:defined_traits).to(:definition) end it "delegates callbacks to its definition" do null_factory = FactoryBot::NullFactory.new expect(null_factory).to delegate(:callbacks).to(:definition) end it "delegates attributes to its definition" do null_factory = FactoryBot::NullFactory.new expect(null_factory).to delegate(:attributes).to(:definition) end it "delegates constructor to its definition" do null_factory = FactoryBot::NullFactory.new expect(null_factory).to delegate(:constructor).to(:definition) end it "has a nil value for its compile attribute" do null_factory = FactoryBot::NullFactory.new expect(null_factory.compile).to be_nil end it "has a nil value for its class_name attribute" do null_factory = FactoryBot::NullFactory.new expect(null_factory.class_name).to be_nil end it "has an instance of FactoryBot::AttributeList for its attributes attribute" do null_factory = FactoryBot::NullFactory.new expect(null_factory.attributes).to be_an_instance_of(FactoryBot::AttributeList) end it "has FactoryBot::Evaluator as its evaluator class" do null_factory = FactoryBot::NullFactory.new expect(null_factory.evaluator_class).to eq FactoryBot::Evaluator end end factory_bot-6.5.6/spec/factory_bot/null_object_spec.rb000066400000000000000000000014431507676517300231720ustar00rootroot00000000000000describe FactoryBot::NullObject do it "responds to the given methods" do methods_to_respond_to = %w[id age name admin?] null_object = FactoryBot::NullObject.new(methods_to_respond_to) methods_to_respond_to.each do |method_name| expect(null_object.__send__(method_name)).to be_nil expect(null_object).to respond_to(method_name) end end it "does not respond to other methods" do methods_to_respond_to = %w[id age name admin?] methods_to_not_respond_to = %w[email date_of_birth title] null_object = FactoryBot::NullObject.new(methods_to_respond_to) methods_to_not_respond_to.each do |method_name| expect { null_object.__send__(method_name) }.to raise_error(NoMethodError) expect(null_object).not_to respond_to(method_name) end end end factory_bot-6.5.6/spec/factory_bot/registry_spec.rb000066400000000000000000000062661507676517300225520ustar00rootroot00000000000000describe FactoryBot::Registry do it "is an enumerable" do registry = FactoryBot::Registry.new("Great thing") expect(registry).to be_kind_of(Enumerable) end it "finds a registered object" do registry = FactoryBot::Registry.new("Great thing") registered_object = double("registered object") registry.register(:object_name, registered_object) expect(registry.find(:object_name)).to eq registered_object end it "finds a registered object with square brackets" do registry = FactoryBot::Registry.new("Great thing") registered_object = double("registered object") registry.register(:object_name, registered_object) expect(registry[:object_name]).to eq registered_object end it "raises when an object cannot be found" do registry = FactoryBot::Registry.new("Great thing") expect { registry.find(:object_name) } .to raise_error(KeyError, "Great thing not registered: \"object_name\"") end it "includes a did_you_mean message" do registry = FactoryBot::Registry.new(:registry) registered_object = double(:registered_object) registry.register(:factory_bot, registered_object) expect { registry.find(:factory_bit) }.to raise_did_you_mean_error end it "adds and returns the object registered" do registry = FactoryBot::Registry.new("Great thing") registered_object = double("registered object") expect(registry.register(:object_name, registered_object)).to eq registered_object end it "knows that an object is registered by symbol" do registry = FactoryBot::Registry.new("Great thing") registered_object = double("registered object") registry.register(:object_name, registered_object) expect(registry).to be_registered(:object_name) end it "knows that an object is registered by string" do registry = FactoryBot::Registry.new("Great thing") registered_object = double("registered object") registry.register(:object_name, registered_object) expect(registry).to be_registered("object_name") end it "knows when an object is not registered" do registry = FactoryBot::Registry.new("Great thing") expect(registry).not_to be_registered("bogus") end it "iterates registered objects" do registry = FactoryBot::Registry.new("Great thing") registered_object = double("registered object") second_registered_object = double("second registered object") registry.register(:first_object, registered_object) registry.register(:second_object, second_registered_object) expect(registry.to_a).to eq [registered_object, second_registered_object] end it "does not include duplicate objects with registered under different names" do registry = FactoryBot::Registry.new("Great thing") registered_object = double("registered object") registry.register(:first_object, registered_object) registry.register(:second_object, registered_object) expect(registry.to_a).to eq [registered_object] end it "clears registered factories" do registry = FactoryBot::Registry.new("Great thing") registered_object = double("registered object") registry.register(:object_name, registered_object) registry.clear expect(registry.count).to be_zero end end factory_bot-6.5.6/spec/factory_bot/sequence_spec.rb000066400000000000000000000155631507676517300225120ustar00rootroot00000000000000shared_examples "a sequence" do |options| first_value = options[:first_value] second_value = options[:second_value] it "has a next value equal to its first value" do expect(subject.next).to eq first_value end it "has a next value equal to the 2nd value after being incremented" do subject.next expect(subject.next).to eq second_value end it "has a next value equal to the 1st value after rewinding" do subject.next subject.rewind expect(subject.next).to eq first_value end end describe FactoryBot::Sequence do describe ".find" do before(:each) do define_class("User") { attr_accessor :name } FactoryBot.define do factory :user do trait :with_email do sequence(:email) { |n| "user_#{n}@example.com" } end end end end it "accepts a list of symbols" do expect(described_class.find(:user, :with_email, :email)).to be_truthy end it "accepts a list of strings" do expect(described_class.find("user", "with_email", "email")).to be_truthy end it "accepts a mixture of symbols & strings" do expect(described_class.find(:user, "with_email", :email)).to be_truthy end it "returns nil with a non-matching URI" do expect(described_class.find(:user, :email)).to be_nil end it "raises an exception with no arguments given" do expect { described_class.find } .to raise_error ArgumentError, /wrong number of arguments, expected 1\+/ end end describe ".find_by_uri" do before(:each) do define_class("User") { attr_accessor :name } FactoryBot.define do factory :user do trait :with_email do sequence(:email) { |n| "user_#{n}@example.com" } end end end end it "accepts a String" do expect(described_class.find_by_uri("user/with_email/email")).to be_truthy end it "accepts a Symbol" do expect(described_class.find_by_uri(:"user/with_email/email")).to be_truthy end it "returns nil with a non-matching URI" do expect(described_class.find_by_uri("user/email")).to be_nil end it "raises an exception with no arguments given" do expect { described_class.find_by_uri } .to raise_error ArgumentError, /wrong number of arguments \(given 0, expected 1\)/ end end describe "a basic sequence" do let(:name) { :test } subject { FactoryBot::Sequence.new(name) { |n| "=#{n}" } } its(:name) { should eq name } its(:names) { should eq [name] } it_behaves_like "a sequence", first_value: "=1", second_value: "=2" end describe "a custom sequence" do subject { FactoryBot::Sequence.new(:name, "A") { |n| "=#{n}" } } it_behaves_like "a sequence", first_value: "=A", second_value: "=B" end describe "a sequence with aliases using default value" do subject do FactoryBot::Sequence.new(:test, aliases: [:alias, :other]) do |n| "=#{n}" end end it "has the expected names as its names" do names = [:foo, :bar, :baz] sequence = FactoryBot::Sequence.new(names.first, aliases: names.last(2)) { "=#{n}" } expect(sequence.names).to eq names end it_behaves_like "a sequence", first_value: "=1", second_value: "=2" end describe "a sequence with custom value and aliases" do subject do FactoryBot::Sequence.new(:test, 3, aliases: [:alias, :other]) do |n| "=#{n}" end end it "has the expected names as its names" do names = [:foo, :bar, :baz] sequence = FactoryBot::Sequence.new(names.first, 3, aliases: names.last(2)) { "=#{n}" } expect(sequence.names).to eq names end it_behaves_like "a sequence", first_value: "=3", second_value: "=4" end describe "a basic sequence without a block" do subject { FactoryBot::Sequence.new(:name) } it_behaves_like "a sequence", first_value: 1, second_value: 2 end describe "a custom sequence without a block" do subject { FactoryBot::Sequence.new(:name, "A") } it_behaves_like "a sequence", first_value: "A", second_value: "B" end describe "a sequence with lazy initial value and a block" do subject do FactoryBot::Sequence.new(:test, proc { "J" }) do |n| "=#{n}" end end it_behaves_like "a sequence", first_value: "=J", second_value: "=K" end describe "a sequence with a lazy initial value without a block" do subject do FactoryBot::Sequence.new(:test, proc { 3 }) end it_behaves_like "a sequence", first_value: 3, second_value: 4 end describe "iterating over items in an enumerator" do subject do FactoryBot::Sequence.new(:name, %w[foo bar].to_enum) { |n| "=#{n}" } end it "navigates to the next items until no items remain" do sequence = FactoryBot::Sequence.new(:name, %w[foo bar].to_enum) { |n| "=#{n}" } expect(sequence.next).to eq "=foo" expect(sequence.next).to eq "=bar" expect { sequence.next }.to raise_error(StopIteration) end it_behaves_like "a sequence", first_value: "=foo", second_value: "=bar" end it "a custom sequence and scope increments within the correct scope" do sequence = FactoryBot::Sequence.new(:name, "A") { |n| "=#{n}#{foo}" } scope = double("scope", foo: "attribute") expect(sequence.next(scope)).to eq "=Aattribute" end it "a custom lazy sequence and scope increments within the correct scope" do sequence = FactoryBot::Sequence.new(:name, proc { "A" }) { |n| "=#{n}#{foo}" } scope = double("scope", foo: "attribute") expect(sequence.next(scope)).to eq "=Aattribute" expect(sequence.next(scope)).to eq "=Battribute" end it "a custom sequence and scope increments within the correct scope when incrementing" do sequence = FactoryBot::Sequence.new(:name, "A") { |n| "=#{n}#{foo}" } scope = double("scope", foo: "attribute") sequence.next(scope) expect(sequence.next(scope)).to eq "=Battribute" end it "a custom lazy sequence and scope increments within the correct scope when incrementing" do sequence = FactoryBot::Sequence.new(:name, proc { "A" }) { |n| "=#{n}#{foo}" } scope = double("scope", foo: "attribute") sequence.next(scope) expect(sequence.next(scope)).to eq "=Battribute" end it "a custom scope increments within the correct scope after rewinding" do sequence = FactoryBot::Sequence.new(:name, "A") { |n| "=#{n}#{foo}" } scope = double("scope", foo: "attribute") sequence.next(scope) sequence.rewind expect(sequence.next(scope)).to eq "=Aattribute" end it "a custom scope with a lazy sequence increments within the correct scope after rewinding" do sequence = FactoryBot::Sequence.new(:name, proc { "A" }) { |n| "=#{n}#{foo}" } scope = double("scope", foo: "attribute") sequence.next(scope) sequence.rewind expect(sequence.next(scope)).to eq "=Aattribute" end end factory_bot-6.5.6/spec/factory_bot/strategy/000077500000000000000000000000001507676517300211735ustar00rootroot00000000000000factory_bot-6.5.6/spec/factory_bot/strategy/attributes_for_spec.rb000066400000000000000000000007251507676517300255720ustar00rootroot00000000000000describe FactoryBot::Strategy::AttributesFor do let(:result) { {name: "John Doe", gender: "Male", admin: false} } let(:evaluation) { double("evaluation", hash: result) } it_should_behave_like "strategy without association support" it "returns the hash from the evaluation" do expect(subject.result(evaluation)).to eq result end it "does not run the to_create block" do expect { subject.result(evaluation) }.to_not raise_error end end factory_bot-6.5.6/spec/factory_bot/strategy/build_spec.rb000066400000000000000000000003621507676517300236320ustar00rootroot00000000000000describe FactoryBot::Strategy::Build do it_should_behave_like "strategy with association support", :create it_should_behave_like "strategy with callbacks", :after_build it_should_behave_like "strategy with strategy: :build", :build end factory_bot-6.5.6/spec/factory_bot/strategy/create_spec.rb000066400000000000000000000006771507676517300240070ustar00rootroot00000000000000describe FactoryBot::Strategy::Create do it_should_behave_like "strategy with association support", :create it_should_behave_like "strategy with callbacks", :after_build, :before_create, :after_create it "runs a custom create block" do evaluation = double( "evaluation", object: nil, notify: nil, create: nil ) subject.result(evaluation) expect(evaluation).to have_received(:create).once end end factory_bot-6.5.6/spec/factory_bot/strategy/stub_spec.rb000066400000000000000000000051511507676517300235110ustar00rootroot00000000000000shared_examples "disabled persistence method" do |method_name| let(:instance) { described_class.new.result(evaluation) } describe "overriding persistence method: ##{method_name}" do it "overrides the method with any arity" do method = instance.method(method_name) expect(method.arity).to eq(-1) end it "raises an informative error if the method is called" do expect { instance.send(method_name) }.to raise_error( RuntimeError, "stubbed models are not allowed to access the database - #{instance.class}##{method_name}()" ) end end end describe FactoryBot::Strategy::Stub do it_should_behave_like "strategy with association support", :build_stubbed it_should_behave_like "strategy with callbacks", :after_stub it_should_behave_like "strategy with strategy: :build", :build_stubbed context "asking for a result" do let(:result_instance) do define_class("ResultInstance") { attr_accessor :id, :created_at }.new end let(:evaluation) do double("evaluation", object: result_instance, notify: true) end it { expect(subject.result(evaluation)).not_to be_new_record } it { expect(subject.result(evaluation)).to be_persisted } it { expect(subject.result(evaluation)).not_to be_destroyed } it "assigns created_at" do created_at1 = subject.result(evaluation).created_at created_at2 = subject.result(evaluation).created_at expect(created_at1).to equal created_at2 end include_examples "disabled persistence method", :connection include_examples "disabled persistence method", :decrement! include_examples "disabled persistence method", :delete include_examples "disabled persistence method", :destroy include_examples "disabled persistence method", :destroy! include_examples "disabled persistence method", :increment! include_examples "disabled persistence method", :reload include_examples "disabled persistence method", :save include_examples "disabled persistence method", :save! include_examples "disabled persistence method", :toggle! include_examples "disabled persistence method", :touch include_examples "disabled persistence method", :update include_examples "disabled persistence method", :update! include_examples "disabled persistence method", :update_attribute include_examples "disabled persistence method", :update_attributes include_examples "disabled persistence method", :update_attributes! include_examples "disabled persistence method", :update_column include_examples "disabled persistence method", :update_columns end end factory_bot-6.5.6/spec/factory_bot/strategy_spec.rb000066400000000000000000000015541507676517300225370ustar00rootroot00000000000000describe FactoryBot::Strategy do it "returns the class passed when it is instantiated with a class" do strategy = define_class("MyAwesomeClass") result = described_class.lookup_strategy(strategy) expect(result).to eq strategy end it "finds the strategy by name when instantiated with a symbol" do strategy = define_class("MyAwesomeClass") allow(FactoryBot::Internal).to receive(:strategy_by_name).and_return(strategy) described_class.lookup_strategy(:build) expect(FactoryBot::Internal).to have_received(:strategy_by_name).with(:build) end it "returns the strategy found when instantiated with a symbol" do strategy = define_class("MyAwesomeClass") allow(FactoryBot::Internal).to receive(:strategy_by_name).and_return(strategy) result = described_class.lookup_strategy(:build) expect(result).to eq strategy end end factory_bot-6.5.6/spec/factory_bot/uri_manager_spec.rb000066400000000000000000000070361507676517300231670ustar00rootroot00000000000000describe FactoryBot::UriManager do describe ".build_uri" do it "combines the parts to form a Symbol" do expect(described_class.build_uri(:ep1, :ep2, :ep3)) .to eq :"ep1/ep2/ep3" end it "works with a single part" do expect(described_class.build_uri(:ep1)) .to eq :ep1 end it "works with multiple arrays of parts" do expect(described_class.build_uri(%i[ep1 ep2], %i[ep3 ep4])) .to eq :"ep1/ep2/ep3/ep4" end it "returns nil when no parts provided" do expect(described_class.build_uri).to be_nil end it "removes leading and trailing slashes" do expect(described_class.build_uri("/start", "end/")) .to eq :"start/end" end it "converts space to underlines" do expect(described_class.build_uri("starting now", "the end is nigh")) .to eq :"starting___now/the_end_is_____nigh" end end describe "#initialize" do context "when only endpoints are provided" do it "creates one URI for each endpoint" do uri_manager = FactoryBot::UriManager.new(:ep1, :ep2, :ep3) expect(uri_manager.uri_list).to eq %i[ep1 ep2 ep3] end it "each URI is a Symbol" do uri_manager = FactoryBot::UriManager.new("ep1", "ep2", "ep3") expect(uri_manager.uri_list).to eq %i[ep1 ep2 ep3] end it "accepts a combination of Symbols & Strings" do uri_manager = FactoryBot::UriManager.new(:ep1, "ep2", :ep3) expect(uri_manager.uri_list).to eq %i[ep1 ep2 ep3] end it "replaces spaces with underlines" do uri_manager = FactoryBot::UriManager.new("ep 1", "e p 2", :ep3) expect(uri_manager.uri_list).to eq %i[ep_1 e_p_2 ep3] end it "stringifies endpoints with non-symbol characters" do uri_manager = FactoryBot::UriManager.new("ep @ 1", "e + 2") expect(uri_manager.uri_list).to eq %i[ep_@_1 e_+_2] end it "accepts a single endpoint" do uri_manager = FactoryBot::UriManager.new(:ep1) expect(uri_manager.uri_list).to eq [:ep1] end it "accepts an array of endpoints" do uri_manager = FactoryBot::UriManager.new([:ep1, "ep2", :ep3]) expect(uri_manager.uri_list).to eq %i[ep1 ep2 ep3] end it "fails with no endpoints given" do expect { FactoryBot::UriManager.new } .to raise_error ArgumentError, /wrong number of arguments \(given 0, expected 1\+\)/ end end context "when paths are also provided" do it "creates one URI for each path/endpoint combination" do uri_manager = FactoryBot::UriManager.new(:e1, :e2, paths: %i[p1 p2]) expect(uri_manager.uri_list).to eq %i[p1/e1 p2/e1 p1/e2 p2/e2] end it "accepts a combination of Symbol & String paths" do uri_manager = FactoryBot::UriManager.new(:e1, "e2", paths: [:p1, "path"]) expect(uri_manager.uri_list).to eq %i[p1/e1 path/e1 p1/e2 path/e2] end it "replaces spaces with underlines" do uri_manager = FactoryBot::UriManager.new(:e1, paths: ["path 1", "path 2"]) expect(uri_manager.uri_list).to eq %i[path_1/e1 path_2/e1] end it "accepts a single path" do uri_manager = FactoryBot::UriManager.new(:e1, :e2, paths: :test_path) expect(uri_manager.uri_list).to eq %i[test_path/e1 test_path/e2] end it "accepts an array of arrays of paths" do uri_manager = FactoryBot::UriManager.new(:e1, paths: [%i[p1 p2], [:p3]]) expect(uri_manager.uri_list).to eq %i[p1/e1 p2/e1 p3/e1] end end end end factory_bot-6.5.6/spec/factory_bot_spec.rb000066400000000000000000000005511507676517300206710ustar00rootroot00000000000000describe FactoryBot do it "finds a registered strategy" do FactoryBot.register_strategy(:strategy_name, :strategy_class) expect(FactoryBot.strategy_by_name(:strategy_name)) .to eq :strategy_class end describe ".use_parent_strategy" do it "is true by default" do expect(FactoryBot.use_parent_strategy).to be true end end end factory_bot-6.5.6/spec/spec_helper.rb000066400000000000000000000017431507676517300176410ustar00rootroot00000000000000# Set timeout when setting sequences require "rspec" require "rspec/its" require "simplecov" if RUBY_ENGINE == "ruby" require "factory_bot" FactoryBot.sequence_setting_timeout = 0.5 if RUBY_ENGINE == "jruby" # Workaround for issue in I18n/JRuby combo. # See https://github.com/jruby/jruby/issues/6547 and # https://github.com/ruby-i18n/i18n/issues/555 require "i18n/backend" require "i18n/backend/simple" end Dir["spec/support/**/*.rb"].each { |f| require File.expand_path(f) } RSpec.configure do |config| config.mock_with :rspec do |mocks| # Prevents you from mocking or stubbing a method that does not exist on a # real object. This is generally recommended, and will default to `true` in # RSpec 4. mocks.verify_partial_doubles = true end config.include DeclarationMatchers config.before do FactoryBot.reload end config.order = :random Kernel.srand config.seed config.example_status_persistence_file_path = "tmp/rspec_examples.txt" end factory_bot-6.5.6/spec/support/000077500000000000000000000000001507676517300165325ustar00rootroot00000000000000factory_bot-6.5.6/spec/support/containers/000077500000000000000000000000001507676517300206775ustar00rootroot00000000000000factory_bot-6.5.6/spec/support/containers/test_log.rb000066400000000000000000000010071507676517300230420ustar00rootroot00000000000000require "forwardable" ## # Designed for tests to log output for later evaluation # module TestLog class << self extend Forwardable def_delegators :log_array, :<<, :[], :size, :first, :last def_delegators :log_array, :map, :in?, :include?, :inspect def all Thread.current[:my_thread_safe_test_log] ||= [] end def reset! Thread.current[:my_thread_safe_test_log] = [] end private def log_array Thread.current[:my_thread_safe_test_log] ||= [] end end end factory_bot-6.5.6/spec/support/macros/000077500000000000000000000000001507676517300200165ustar00rootroot00000000000000factory_bot-6.5.6/spec/support/macros/define_constant.rb000066400000000000000000000027431507676517300235140ustar00rootroot00000000000000require "active_record" module DefineConstantMacros def define_class(path, base = Object, &block) const = stub_const(path, Class.new(base)) const.class_eval(&block) if block const end def define_model(name, columns = {}, &block) model = define_class(name, ActiveRecord::Base, &block) create_table(model.table_name) do |table| columns.each do |column_name, type| table.column column_name, type end end model end def create_table(table_name, &block) connection = ActiveRecord::Base.connection begin connection.execute("DROP TABLE IF EXISTS #{table_name}") connection.create_table(table_name, &block) created_tables << table_name connection rescue Exception => e # rubocop:disable Lint/RescueException connection.execute("DROP TABLE IF EXISTS #{table_name}") raise e end end def clear_generated_tables created_tables.each do |table_name| clear_generated_table(table_name) end created_tables.clear end def clear_generated_table(table_name) ActiveRecord::Base .connection .execute("DROP TABLE IF EXISTS #{table_name}") end private def created_tables @created_tables ||= [] end end RSpec.configure do |config| config.include DefineConstantMacros config.before(:all) do ActiveRecord::Base.establish_connection( adapter: "sqlite3", database: ":memory:" ) end config.after do clear_generated_tables end end factory_bot-6.5.6/spec/support/macros/deprecation.rb000066400000000000000000000003461507676517300226430ustar00rootroot00000000000000require "active_support" RSpec.configure do |config| config.around :example, silence_deprecation: true do |example| with_temporary_assignment(FactoryBot::Deprecation, :silenced, true) do example.run end end end factory_bot-6.5.6/spec/support/macros/temporary_assignment.rb000066400000000000000000000006271507676517300246220ustar00rootroot00000000000000module TemporaryAssignment def with_temporary_assignment(assignee, attribute, temporary_value) original_value = assignee.public_send(attribute) attribute_setter = "#{attribute}=" assignee.public_send(attribute_setter, temporary_value) yield ensure assignee.public_send(attribute_setter, original_value) end end RSpec.configure do |config| config.include TemporaryAssignment end factory_bot-6.5.6/spec/support/matchers/000077500000000000000000000000001507676517300203405ustar00rootroot00000000000000factory_bot-6.5.6/spec/support/matchers/be_about_now.rb000066400000000000000000000001761507676517300233340ustar00rootroot00000000000000RSpec::Matchers.define :be_about_now do match do |actual| expect(actual).to be_within(2.seconds).of(Time.now) end end factory_bot-6.5.6/spec/support/matchers/callback.rb000066400000000000000000000003431507676517300224210ustar00rootroot00000000000000RSpec::Matchers.define :have_callback do |callback_name| match do |instance| instance.callbacks.include?(FactoryBot::Callback.new(callback_name, @block)) end chain :with_block do |block| @block = block end end factory_bot-6.5.6/spec/support/matchers/declaration.rb000066400000000000000000000031251507676517300231530ustar00rootroot00000000000000module DeclarationMatchers def have_dynamic_declaration(name) DeclarationMatcher.new(:dynamic).named(name) end def have_association_declaration(name) DeclarationMatcher.new(:association).named(name) end def have_implicit_declaration(name) DeclarationMatcher.new(:implicit).named(name) end class DeclarationMatcher def initialize(declaration_type) @declaration_type = declaration_type end def matches?(subject) subject.declarations.include?(expected_declaration) end def named(name) @name = name self end def ignored @ignored = true self end def with_value(value) @value = value self end def with_factory(factory) @factory = factory self end def with_options(options) @options = options self end def failure_message [ "expected declarations to include declaration of type #{@declaration_type}", @options ? "with options #{options}" : nil ].compact.join " " end private def expected_declaration case @declaration_type when :dynamic then FactoryBot::Declaration::Dynamic.new(@name, ignored?, @value) when :implicit then FactoryBot::Declaration::Implicit.new(@name, @factory, ignored?) when :association if @options FactoryBot::Declaration::Association.new(@name, options) else FactoryBot::Declaration::Association.new(@name) end end end def ignored? !!@ignored end def options @options || {} end end end factory_bot-6.5.6/spec/support/matchers/delegate.rb000066400000000000000000000021131507676517300224340ustar00rootroot00000000000000RSpec::Matchers.define :delegate do |delegated_method| chain :to do |target_method| @target_method = target_method end chain :as do |method_on_target| @method_on_target = method_on_target end chain :with_arguments do |args| @args = args end match do |instance| @instance = instance @args ||= [] return_value = "stubbed return value" method_on_target = @method_on_target || delegated_method stubbed_target = double("stubbed_target", method_on_target => return_value) allow(@instance).to receive(@target_method).and_return stubbed_target begin @instance.send(delegated_method, *@args) == return_value rescue NoMethodError false end end failure_message do if Class === @instance message = "expected #{@instance.name} " prefix = "." else message = "expected #{@instance.class.name} " prefix = "#" end message << "to delegate #{prefix}#{delegated_method} to #{prefix}#{@target_method}" if @method_on_target message << ".#{@method_on_target}" end message end end factory_bot-6.5.6/spec/support/matchers/raise_did_you_mean_error.rb000066400000000000000000000010301507676517300257070ustar00rootroot00000000000000RSpec::Matchers.define :raise_did_you_mean_error do supports_block_expectations match do |actual| # detailed_message introduced in Ruby 3.2 for cleaner integration with # did_you_mean. See https://bugs.ruby-lang.org/issues/18564 matcher = if KeyError.method_defined?(:detailed_message) raise_error( an_instance_of(KeyError) .and(having_attributes(detailed_message: /Did you mean\?/)) ) else raise_error(KeyError, /Did you mean\?/) end expect(&actual).to matcher end end factory_bot-6.5.6/spec/support/matchers/trait.rb000066400000000000000000000004041507676517300220060ustar00rootroot00000000000000RSpec::Matchers.define :have_trait do |trait_name| match do |instance| instance.defined_traits.any? do |trait| trait.name == trait_name.to_s && trait.send(:block) == @block end end chain :with_block do |block| @block = block end end factory_bot-6.5.6/spec/support/shared_examples/000077500000000000000000000000001507676517300216765ustar00rootroot00000000000000factory_bot-6.5.6/spec/support/shared_examples/strategy.rb000066400000000000000000000060511507676517300240670ustar00rootroot00000000000000shared_examples_for "strategy without association support" do let(:factory) { double("associate_factory") } let(:attribute) { FactoryBot::Attribute::Association.new(:user, :user, {}) } def association_named(name, overrides) runner = FactoryBot::FactoryRunner.new(name, :build, [overrides]) subject.association(runner) end before do allow(FactoryBot::Internal).to receive(:factory_by_name).and_return factory allow(factory).to receive(:compile) allow(factory).to receive(:run) end it "returns nil when accessing an association" do expect(association_named(:user, {})).to be_nil end end shared_examples_for "strategy with association support" do |factory_bot_strategy_name| let(:factory) { double("associate_factory") } def association_named(name, strategy, overrides) runner = FactoryBot::FactoryRunner.new(name, strategy, [overrides]) subject.association(runner) end before do allow(FactoryBot::Internal).to receive(:factory_by_name).and_return factory allow(factory).to receive(:compile) allow(factory).to receive(:run) end it "runs the factory with the correct overrides" do association_named(:author, factory_bot_strategy_name, great: "value") expect(factory).to have_received(:run).with(factory_bot_strategy_name, great: "value") end it "finds the factory with the correct factory name" do association_named(:author, factory_bot_strategy_name, great: "value") expect(FactoryBot::Internal).to have_received(:factory_by_name).with(:author) end end shared_examples_for "strategy with strategy: :build" do |factory_bot_strategy_name| let(:factory) { double("associate_factory") } def association_named(name, overrides) runner = FactoryBot::FactoryRunner.new(name, overrides[:strategy], [overrides.except(:strategy)]) subject.association(runner) end before do allow(FactoryBot::Internal).to receive(:factory_by_name).and_return factory allow(factory).to receive(:compile) allow(factory).to receive(:run) end it "runs the factory with the correct overrides" do association_named(:author, strategy: :build, great: "value") expect(factory).to have_received(:run).with(factory_bot_strategy_name, great: "value") end it "finds the factory with the correct factory name" do association_named(:author, strategy: :build, great: "value") expect(FactoryBot::Internal).to have_received(:factory_by_name).with(:author) end end shared_examples_for "strategy with callbacks" do |*callback_names| let(:result_instance) do define_class("ResultInstance") { attr_accessor :id }.new end let(:evaluation) do double("evaluation", object: result_instance, notify: true, create: nil) end it "runs the callbacks #{callback_names} with the evaluation's object" do subject.result(evaluation) callback_names.each do |name| expect(evaluation).to have_received(:notify).with(name, evaluation.object) end end it "returns the object from the evaluation" do expect(subject.result(evaluation)).to eq evaluation.object end end