{"id":13463372,"url":"https://github.com/rdy/fixture_builder","last_synced_at":"2025-03-25T06:31:53.583Z","repository":{"id":561846,"uuid":"192820","full_name":"rdy/fixture_builder","owner":"rdy","description":"fixture builder based on code from fixture scenarios","archived":false,"fork":false,"pushed_at":"2023-10-23T19:44:26.000Z","size":117,"stargazers_count":162,"open_issues_count":14,"forks_count":64,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-05-10T19:42:16.952Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rdy.png","metadata":{"files":{"readme":"README.markdown","changelog":null,"contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2009-05-05T06:20:07.000Z","updated_at":"2024-03-28T13:10:13.000Z","dependencies_parsed_at":"2024-01-13T03:01:15.377Z","dependency_job_id":"d3097d26-4d1a-4817-8018-06970d7f6257","html_url":"https://github.com/rdy/fixture_builder","commit_stats":{"total_commits":121,"total_committers":35,"mean_commits":"3.4571428571428573","dds":0.7603305785123967,"last_synced_commit":"13c60da9c4cf3b3d8bc52bb36bb383c6b28a8204"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdy%2Ffixture_builder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdy%2Ffixture_builder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdy%2Ffixture_builder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdy%2Ffixture_builder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rdy","download_url":"https://codeload.github.com/rdy/fixture_builder/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222045605,"owners_count":16921980,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T13:00:52.228Z","updated_at":"2024-10-29T12:31:15.776Z","avatar_url":"https://github.com/rdy.png","language":"Ruby","readme":"FixtureBuilder\n==============\n\n[![Build Status](https://secure.travis-ci.org/rdy/fixture_builder.png)](http://travis-ci.org/rdy/fixture_builder)\n\nBased on the code from fixture_scenarios, by Chris Wanstrath.  Allows you to build file fixtures from\nexisting object mother factories, like FactoryGirl, to generate high performance fixtures that can be\nshared across all your tests and development environment.\n\nThe best of all worlds!\n\n* **Speed**: Leverage the high-performance speed of Rails' transactional tests/fixtures to avoid test suite slowdown\n  as your app's number of tests grows, because [creating and persisting data is slow!](https://robots.thoughtbot.com/speed-up-tests-by-selectively-avoiding-factory-girl)\n* **Maintainability/Reuse/Abstraction**: Use object mother factories to generate fixtures via\n  FactoryGirl or your favorite tool\n* **Flexibility**: You can always fall back to object mothers in tests if needed, or load a fixture\n  and modify only an attribute or two without the overhead of creating an entire object dependency graph.\n* **Consistency**: Use the exact same fixture data in all environments: test, development, and demo/staging servers.\n  Makes reproduction and acceptance testing of bugs/features faster and easier!\n* **Simplicity**: Avoid having to maintain and generate `seeds.rb` sample data set separately from your test fixture/factory data set,\n  or [pick which of the myriad seeds helper gems to use](https://rubygems.org/search?query=seed).  *Just delete\n  `seeds.rb` and forget about it!*\n\nInstalling\n==========\n\n 1. Install:\n   * Directly: `gem install fixture_builder`\n   * Bundler:\n   \n     ```ruby\n     # Gemfile\n     group :development, :test do\n       gem 'fixture_builder'\n\n     ```     \n 1. Create a file which configures and declares your fixtures (see below for examples)\n 1. Require the above file in your `spec_helper.rb` or `test_helper.rb`\n 1. If you are using rspec, ensure you have\n    * Set the `FIXTURES_PATH` in `config/application.rb` (not test.rb, or you can't use `rake db:fixtures:load`). E.g.:\n\n      ```ruby\n      module MyApp\n       class Application \u003c Rails::Application\n         #...\n         ENV['FIXTURES_PATH'] = 'spec/fixtures'\n         #...\n      ```\n    * Set `config.fixture_path = Rails.root.join('spec/fixtures')` in `spec/rails_helper.rb`\n    * Set `config.global_fixtures = :all` if you don't want to specify fixtures in every spec file.\n 1. You probably also want to use [**config.use_transactional_fixtures**](https://www.relishapp.com/rspec/rspec-rails/docs/transactions)\n    (if you are using rspec)\n    or [**use_transactional_fixtures/use_transactional_tests**](http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html) (if you are not using rspec),\n 1. If you are using fixtures in Selenium-based Capybara/Cucumber specs that runs the tests and server in separate processes,\n    you probably want to consider setting transactional fixtures to false, and instead using\n    [Database Cleaner](https://github.com/DatabaseCleaner/database_cleaner) \n    with `DatabaseCleaner.strategy = :truncation` or `DatabaseCleaner.strategy = :deletion`.\n\nUsage\n=====\n\n* When running tests/specs, fixtures will build/rebuild automatically as needed\n* `rake spec:fixture_builder:build` to force a build of fixtures\n* `rake spec:fixture_builder:clean` to delete all existing fixture files\n* `rake spec:fixture_builder:rebuild` to force a rebuild of fixtures (just a clean + build)\n* `rake db:fixtures:load` to load built fixtures into your development environment (this is a standard Rails rake task)\n\nConfiguration Example\n=====================\n\n`spec/rails_helper.rb` or `test/test_helper.rb`:\n\n```ruby\nrequire_relative 'support/fixture_builder'\n```\n\nWhen using an object mother such as factory_girl it can be setup like the following:\n\n```ruby\n# spec/support/fixture_builder.rb \nFixtureBuilder.configure do |fbuilder|\n  # rebuild fixtures automatically when these files change:\n  fbuilder.files_to_check += Dir[\"spec/factories/*.rb\", \"spec/support/fixture_builder.rb\"]\n\n  # now declare objects\n  fbuilder.factory do\n    david = Factory(:user, :unique_name =\u003e \"david\")\n    ipod = Factory(:product, :name =\u003e \"iPod\")\n    Factory(:purchase, :user =\u003e david, :product =\u003e ipod)\n  end\nend\n```    \n\nThe block passed to the factory method initiates the creation of the fixture files.\nBefore yielding to the block, FixtureBuilder cleans out the test database completely.\nWhen the block finishes, it dumps the state of the database into fixtures, like this:\n\n```yaml\n# users.yml\ndavid:\n  created_at: 2010-09-18 17:21:23.926511 Z\n  unique_name: david\n  id: 1\n\n# products.yml\ni_pod:\n  name: iPod\n  id: 1\n\n# purchases.yml\npurchase_001:\n  product_id: 1\n  user_id: 1\n```\n\nFixtureBuilder guesses about how to name fixtures based on a prioritized list of attribute names.\nYou can also hint at a name or manually name an object.  Both of the following lines would\nwork to rename `purchase_001` to `davids_ipod`:\n\n```ruby\nfbuilder.name(:davids_ipod, Factory(:purchase, :user =\u003e david, :product =\u003e ipod))\n@davids_ipod = Factory(:purchase, :user =\u003e david, :product =\u003e ipod)\n```\n\nAnother way to name fixtures is to use the name_model_with. To use it you create a block that\nreturns how you want a certain model name based on the record field.\n\n```ruby\nfbuilder.name_model_with(User) do |record|\n  [record['first_name'], record['last_name']].join('_')\nend\n```\n\nFor all User fixture {first_name: 'foo', last_name: 'bar'} it would generate `foo_bar` as the fixture name.\n\nThere are also additional configuration options that can be changed to override the defaults:\n\n* files_to_check: array of filenames that when changed cause fixtures to be rebuilt\n* fixture_builder_file: the pathname of the file used to store file changes.\n* record_name_fields: array of field names to use as a fixture's name prefix, it will use the first matching field it finds\n* skip_tables: array of table names to skip building fixtures\n* select_sql: sql string to use for select\n* delete_sql: sql string to use for deletes\n\nBy default these are set as:\n\n* files_to_check: %w{ db/schema.rb }\n* fixture_builder_file: RAILS_ROOT/tmp/fixture_builder.yml\n* record_name_fields: %w{ unique_name display_name name title username login }\n* skip_tables: %w{ schema_migrations ar_internal_metadata }\n* select_sql: SELECT * FROM %{table}\n* delete_sql: DELETE FROM %{table}\n\nSequence Collisions\n===================\n\nOne problem with generating your fixtures is that sequences can collide.\nWhen the fixtures are generated only as needed, sometimes the process that\ngenerates the fixtures will be different than the process that runs the tests.\nThis results in collisions when you still use factories in your tests.\n\nThere's a couple of solutions for this.\n\nHere's a solution for FactoryGirl which resets sequences numbers to 1000\n(to avoid conflicts with fixture data which should be sequenced \u003c 1000)\nbefore the tests run:\n\n```ruby\nFixtureBuilder.configure do |fbuilder|\n  # ...\nend\n\n# Have factory girl generate non-colliding sequences starting at 1000 for data created after the fixtures\n\n# Factory Girl \u003c2 yields name \u0026 seq\n# Factory Girl \u003e2 yeilds only seq\nFactoryGirl.sequences.each do |seq|\n \n  # Factory Girl 4 uses an Enumerator Adapter, otherwise simply set a Fixnum\n  seq.instance_variable_set(:@value, FactoryGirl::Sequence::EnumeratorAdapter.new(1000))\n  \nend\n```\n\nAnother solution is to explicitly reset the database primary key sequence via ActiveRecord.\nYou could call this method before you run your factories in the `fixture_builder.rb` block:\n\n```ruby\ndef reset_pk_sequences\n  puts 'Resetting Primary Key sequences'\n  ActiveRecord::Base.connection.tables.each do |t|\n    ActiveRecord::Base.connection.reset_pk_sequence!(t)\n  end\nend\n\n```\n\nIt's probably a good idea to use both of these approaches together, especially if you are\ngoing to fall back to using FactoryGirl object mothers in addition to fixtures.\n\nTips\n====\n\n* Don't use `seeds.rb` (just delete it).  Instead, just use `rake db:fixtures:load` to get fixtures into dev.\n* If you want fixture data on a staging/demo environment, either run `db:fixtures:load` on that environment, or\n  load fixtures into the dev with `rake db:fixtures:load`, dump the dev database, then load it on your environment.\n* Always use fixtures instead of object mothers in tests when possible - this will keep your test suite fast!\n  [Even FactoryGirl says to avoid using factories when you can, because creating and persisting data is slow](https://robots.thoughtbot.com/speed-up-tests-by-selectively-avoiding-factory-girl)\n* If you only need to tweak an attribute or two to test an edge case, load the fixture object,\n  then just set the attribute on the object (if you don't need it persisted, this is fastest), or\n  set it via `#update_attributes!` (only if you need it persisted, this is slower).\n* Avoid referring to any fixtures by ID anywhere, unless you hardcode the ID when creating it.  They can change\n  if you add more fixtures in the future and cause tests to break.\n* To set up associations between different types of created fixture model objects, you can\n  use a couple of approaches:\n  1. When creating fixtures, keep a hash of all created models by type + name (not ID), and then look them up\n     out of the hash to use as an associated object when creating subsequent related objects.\n  1. Do a `MyModel.find_by_some_unique_field` to find a previously created instance that didn't have a name.   \n* If you delete a table, old fixture files for the deleted table can hang around and still get loaded\n  into the database, causing confusion or errors.  Use `rake spec:fixture_builder:clean` or \n  `rake spec:fixture_builder:rebuild` to ensure they get cleaned up.\n* As you build more advanced fixture creation logic for your app's domain and try to DRY it up, you'll probably\n  end up having an easier time if:\n  1. You don't use any namespaced models\n  1. You keep your factory names consistent and exactly matching your model names\n* Modify `bin/setup` to run fixture builder and load your dev database:\n      ```ruby\n      puts \"\\n== Building fixtures ==\"\n      system! 'bin/rails spec:fixture_builder:rebuild'\n        \n      puts \"\\n== Loading fixtures into dev database ==\"\n      system! 'bin/rails db:fixtures:load'\n      ```\n\nMore Complete Config Example\n============================\n\nAs you get more fixtures, you may want to move the creation of fixtures to a separate file.  For example:  \n\n```ruby\n# spec/support/fixture_builder.rb \nrequire_relative 'create_fixtures'\n\nFixtureBuilder.configure do |fbuilder|\n  # rebuild fixtures automatically when these files change:\n  fbuilder.files_to_check += Dir[\n    \"spec/factories/*.rb\",\n    \"spec/support/fixture_builder.rb\",\n    \"spec/support/create_fixtures.rb\",\n  ]\n\n  # now declare objects\n  fbuilder.factory do\n    CreateFixtures.new(fbuilder).create_all\n  end\nend\n\n# Have factory girl generate non-colliding sequences starting at 1000 for data created after the fixtures\nFactoryGirl.sequences.each do |seq|\n  seq.instance_variable_set(:@value, FactoryGirl::Sequence::EnumeratorAdapter.new(1000))\nend\n```\n\nThen, you can do more extensive and advanced fixture creation in that class.  Here's\na partial example:\n\n```ruby\n# spec/support/create_fixtures.rb \n\nrequire 'factory_girl_rails'\n\nclass CreateFixtures\n  include FactoryGirl::Syntax::Methods\n\n  attr_accessor :fbuilder, :models, :fixed_time\n\n  def initialize(fbuilder)\n    @fbuilder = fbuilder\n    @models = {}\n    @fixed_time = Time.utc(2015, 3, 14, 9, 2, 6)\n  end\n\n  def create_all\n    reset_pk_sequences\n    create_users\n    create_products\n    create_purchases\n    reset_pk_sequences\n  end\n\n  private\n\n  def reset_pk_sequences\n    puts 'Resetting Primary Key sequences'\n    ActiveRecord::Base.connection.tables.each do |t|\n      ActiveRecord::Base.connection.reset_pk_sequence!(t)\n    end\n  end\n  \n  def create_users\n    # etc...\n  end \n  \n  # other creation and helper methods to abstract common logic, e.g. \n  # * custom naming rules via #name_model_with\n  # * set up associations by storing created model records in a hash so you can retrieve them\n  # etc... (hopefully some of these helper patterns can be standardized and included in the gem in the future)\n end \n```\n\nCopyright (c) 2009 Ryan Dy \u0026 David Stevenson, released under the MIT license\n\nCurrently maintained by [Chad Woolley](mailto:thewoolleyman@gmail.com)\n","funding_links":[],"categories":["Testing","Ruby"],"sub_categories":["Rails Fixture Replacement"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frdy%2Ffixture_builder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frdy%2Ffixture_builder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frdy%2Ffixture_builder/lists"}