{"id":13879275,"url":"https://github.com/nepalez/fixturama","last_synced_at":"2025-10-19T13:04:35.418Z","repository":{"id":53608455,"uuid":"168320692","full_name":"nepalez/fixturama","owner":"nepalez","description":"Collection of helpers for dealing with fixtures in RSpec","archived":false,"fork":false,"pushed_at":"2021-04-12T15:30:40.000Z","size":103,"stargazers_count":44,"open_issues_count":2,"forks_count":5,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-25T23:22:11.175Z","etag":null,"topics":["rspec","ruby","testing"],"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/nepalez.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-30T10:02:32.000Z","updated_at":"2024-12-21T21:18:40.000Z","dependencies_parsed_at":"2022-08-27T04:50:55.128Z","dependency_job_id":null,"html_url":"https://github.com/nepalez/fixturama","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nepalez%2Ffixturama","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nepalez%2Ffixturama/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nepalez%2Ffixturama/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nepalez%2Ffixturama/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nepalez","download_url":"https://codeload.github.com/nepalez/fixturama/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248512497,"owners_count":21116613,"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":["rspec","ruby","testing"],"created_at":"2024-08-06T08:02:15.915Z","updated_at":"2025-10-19T13:04:30.367Z","avatar_url":"https://github.com/nepalez.png","language":"Ruby","readme":"# Fixturama\n\nCollection of helpers for dealing with fixtures in [RSpec][rspec]\n\nRead the [post about the library on dev.to][dev_to].\n\n\u003ca href=\"https://evilmartians.com/\"\u003e\n\u003cimg src=\"https://evilmartians.com/badges/sponsored-by-evil-martians.svg\" alt=\"Sponsored by Evil Martians\" width=\"236\" height=\"54\"\u003e\u003c/a\u003e\n\n[![Gem Version](https://badge.fury.io/rb/fixturama.svg)][gem]\n[![Build Status](https://travis-ci.org/nepalez/fixturama.svg?branch=master)][travis]\n\n## Installation\n\n```ruby\ngem \"fixturama\"\n```\n\n## Configuration\n\nOn Rails add offsets to id sequences of database tables.\n\n```ruby\n# spec/rails_helper.rb\nRSpec.configure do |config|\n  config.before(:suite) { Fixturama.start_ids_from 1_000_000 }\nend\n```\n\nNow when you hardcode ids in fixtures (under 1_000_000), they won't conflict with authomatically created ones.\n\n## Usage\n\n```ruby\n# spec/spec_helper.rb\nrequire \"fixturama/rspec\"\n```\n\nThe gem defines 3 helpers (support ERB bindings):\n\n- `load_fixture(path, **opts)` to load data from a fixture, and deserialize YAML and JSON\n- `seed_fixture(path_to_yaml, **opts)` to prepare database using the [FactoryBot][factory-bot]\n- `stub_fixture(path_to_yaml, **opts)` to stub some classes\n\n### Loading\n\n```ruby\n# spec/models/user/_spec.rb\nRSpec.describe \"GraphQL mutation 'deleteProfile'\" do\n  subject { Schema.execute(mutation).to_h }\n\n  before do\n    seed_fixture(\"#{__dir__}/database.yml\", profile_id: 42)\n    stub_fixture(\"#{__dir__}/stubs.yml\",    profile_id: 42)\n  end\n\n  let(:mutation) { load_fixture \"#{__dir__}/mutation.graphql\", profile_id: 42 }\n  let(:result)   { load_fixture \"#{__dir__}/result.yaml\" }\n\n  it { is_expected.to eq result }\n\n  it \"deletes the profile\" do\n    expect { subject }.to change { Profile.find_by(id: 42) }.to nil\n  end\n\n  it \"sends a notification\" do\n    expect(Notifier)\n      .to receive_message_chain(:create)\n      .with(\"profileDeleted\", 42)\n\n    subject\n  end\nend\n```\n\nNotice, that since the `v0.0.6` the gem also supports binding any ruby object, not only strings, booleans and numbers:\n\n```yaml\n# ./data.yml\n---\naccount: \u003c%= user %\u003e\n```\n\n```ruby\n# Bind activerecord model\nsubject { load_fixture \"#{__dir__}/data.yml\", user: user }\n\nlet(:user) { FactoryBot.create :user }\n\n# The same object will be returned\nit { is_expected.to eq account: user }\n```\n\nThe object must be named in the options you send to the `load_fixture`, `stub_fixture`, or `seed_fixture` helpers.\n\nThis feature can also be useful to produce a \"partially defined\" fixtures with [RSpec argument matchers][rspec-argument-matchers]:\n\n```ruby\nsubject { load_fixture \"#{__dir__}/data.yml\", user: kind_of(ActiveRecord::Base) }\n```\n\nSince the v0.5.0 we support another way to serialize PORO objects in fixtures. Just wrap them to the `object()` method:\n\n```yaml\n---\n:account: \u003c%= object(user) %\u003e\n```\n\nThis time you don't need sending objects explicitly.\n\n```ruby\nRSpec.describe \"example\" do\n    subject { load_fixture \"#{__dir__}/data.yml\" }\n    \n    let(:user) { FactoryBot.create(:user) }\n    \n    # The same object will be returned\n    it { is_expected.to eq(account: user) }\nend\n```\n\nUnder the hood we use `Marshal.dump` and `Marshal.restore` to serialize and deserialize the object back.\n\n**Notice**, that deserialization creates a new instance of the object which is not equivalent to the source (`user` in the example above)!\nIn most cases this is enough. For example, you can provide matchers like:\n\n```yaml\n---\nnumber: \u003c%= object(be_positive) %\u003e\n```\n\nThe loaded object would contain `{ \"number\" =\u003e be_positive }`.\n\n### Seeding\n\nThe seed (`seed_fixture`) file should be a YAML/JSON with opinionated parameters, namely:\n\n- `type` for the name of the [FactoryBot][factory-bot] factory\n- `traits` for the factory traits\n- `params` for parameters of the factory\n\n```yaml\n# ./database.yml\n#\n# This is the same as\n# `create_list :profile, 1, :active, id: profile_id`\n---\n- type: profile\n  traits:\n    - active\n  params:\n    id: \u003c%= profile_id %\u003e\n```\n\nUse the `count: 2` key to create more objects at once.\n\n### Stubbing\n\nThe gem supports stubbing message chains, constants and http requests with the following keys.\n\nFor message chains:\n\n- `class` for stubbed class\n- `chain` for messages chain\n- `arguments` (optional) for specific arguments\n- `actions` for an array of actions for consecutive invocations of the chain with keys\n    - `return` for a value to be returned\n    - `raise` for an exception to be risen\n    - `repeate` for a number of invocations with this action\n\nFor constants:\n\n- `const` for stubbed constant\n- `value` for a value of the constant\n\nFor environment variables:\n\n- `env` for the name of a variable\n  `value` for a value of the variable\n\nFor http requests:\n\n- `url` or `uri` for the URI of the request (treats values like `/.../` as regular expressions)\n- `method` for the specific http-method (like `get` or `post`)\n- `body` for the request body (treats values like `/.../` as regular expressions)\n- `headers` for the request headers\n- `query` for the request query\n- `basic_auth` for the `user` and `password` of basic authentication\n- `response` or `responses` for consecutively envoked responses with keys:\n    - `status`\n    - `body`\n    - `headers`\n    - `repeate` for the number of times this response should be returned before switching to the next one\n\n```yaml\n# ./stubs.yml\n#\n# The first invocation acts like\n#\n# allow(Notifier)\n#   .to receive_message_chain(:create)\n#   .with(:profileDeleted, 42)\n#   .and_return true\n#\n# then it will act like\n#\n# allow(Notifier)\n#   .to receive_message_chain(:create)\n#   .with(:profileDeleted, 42)\n#   .and_raise ActiveRecord::RecordNotFound\n#\n---\n- class: Notifier\n  chain:\n    - create\n  arguments:\n    - :profileDeleted\n    - \u003c%= profile_id %\u003e\n  actions:\n    - return: true\n      repeate: 1 # this is the default value\n    - raise: ActiveRecord::RecordNotFound\n      arguments:\n        - \"Profile with id: 1 not found\" # for error message\n\n# Here we stubbing a constant\n- const: NOTIFIER_TIMEOUT_SEC\n  value: 10\n\n# This is a stub for ENV['DEFAULT_EMAIL']\n- env: DEFAULT_EMAIL\n  value: foo@example.com\n\n# Examples for stubbing HTTP\n- uri: /example.com/foo/ # regexp!\n  method: delete\n  basic_auth:\n    user: foo\n    password: bar\n  responses:\n    - status: 200 # for the first call\n      repeate: 1   # this is the default value, but you can set another one\n    - status: 404 # for any other call\n\n- uri: htpps://example.com/foo # exact string!\n  method: delete\n  responses:\n    - status: 401\n```\n\n```graphql\nmutation {\n  deleteProfile(\n    input: {\n      id: \"\u003c%= profile_id %\u003e\"\n    }\n  ) {\n    success\n    errors {\n      message\n      fields\n    }\n  }\n}\n```\n\n```yaml\n# ./result.yaml\n---\ndata:\n  deleteProfile:\n    success: true\n    errors: []\n```\n\nWith these helpers all the concrete settings can be extracted to fixtures.\n\nI find it especially helpful when I need to check different edge cases. Instead of polluting a specification with various parameters, I create the sub-folder with \"input\" and \"output\" fixtures for every case.\n\nLooking at the spec I can easily figure out the \"structure\" of expectation, while looking at fixtures I can check the concrete corner cases.\n\n## Single Source of Changes\n\nIf you will, you can list all stubs and seeds at the one single file like\n\n```yaml\n# ./changes.yml\n---\n- type: user\n  params:\n    id: 1\n    name: Andrew\n\n- const: DEFAULT_USER_ID\n  value: 1\n```\n\nThis fixture can be applied via `call_fixture` method just like we did above with `seed_fixture` and `stub_fixture`:\n\n```ruby\nbefore { call_fixture \"#{__dir__}/changes.yml\" }\n```\n\nIn fact, since the `v0.2.0` all those methods are just the aliases of the `call_fixture`.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License][license].\n\n[gem]: https://rubygems.org/gems/fixturama\n[travis]: https://travis-ci.org/nepalez/fixturama\n[license]: http://opensource.org/licenses/MIT\n[factory-bot]: https://github.com/thoughtbot/factory_bot\n[rspec]: https://rspec.info/\n[dev_to]: https://dev.to/evilmartians/a-fixture-based-approach-to-interface-testing-in-rails-2cd4\n[rspec-argument-matchers]: https://relishapp.com/rspec/rspec-mocks/v/3-8/docs/setting-constraints/matching-arguments\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnepalez%2Ffixturama","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnepalez%2Ffixturama","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnepalez%2Ffixturama/lists"}