{"id":15459167,"url":"https://github.com/tylerrick/rspec-expect_to_make_changes","last_synced_at":"2026-05-18T10:35:01.238Z","repository":{"id":56892860,"uuid":"254219834","full_name":"TylerRick/rspec-expect_to_make_changes","owner":"TylerRick","description":"Makes it easy to test that a block makes a number of changes, without requiring you to deeply nest a bunch of `expect { }` blocks within each other or rewrite them as `change` matchers.","archived":false,"fork":false,"pushed_at":"2020-04-09T00:17:18.000Z","size":29,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-08T04:57:47.051Z","etag":null,"topics":["rspec","rspec-expectations","testing-library"],"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/TylerRick.png","metadata":{"files":{"readme":"Readme.md","changelog":"Changelog.md","contributing":null,"funding":null,"license":"License","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-08T23:02:18.000Z","updated_at":"2020-04-09T00:17:12.000Z","dependencies_parsed_at":"2022-08-20T16:10:37.212Z","dependency_job_id":null,"html_url":"https://github.com/TylerRick/rspec-expect_to_make_changes","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/TylerRick/rspec-expect_to_make_changes","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TylerRick%2Frspec-expect_to_make_changes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TylerRick%2Frspec-expect_to_make_changes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TylerRick%2Frspec-expect_to_make_changes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TylerRick%2Frspec-expect_to_make_changes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TylerRick","download_url":"https://codeload.github.com/TylerRick/rspec-expect_to_make_changes/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TylerRick%2Frspec-expect_to_make_changes/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279012639,"owners_count":26085158,"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","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","rspec-expectations","testing-library"],"created_at":"2024-10-01T23:05:09.205Z","updated_at":"2025-10-12T19:06:51.029Z","avatar_url":"https://github.com/TylerRick.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RSpec `expect {…}.to make_changes(…)`\n\nThis small library makes it easy to test that a block makes a number of changes, without requiring\nyou to deeply nest a bunch of `expect { }` blocks within each other or rewrite them as `change`\nmatchers.\n\nSure, you could just write add a list of regular RSpec expectations about how the state should be\n_before_ your action, and another list of expectations about how the state should be _after_ your\naction, and call that a \"test for how your action changes the state\".\n\n```ruby\n  expect(thing.a).to eq 1\n  expect(thing.b).to eq 2\n  expect(thing.c).to eq 3\n  perform_change\n  expect(thing.c).to eq 9\n  expect(thing.b).to eq -2\n  expect(thing.a).to eq 0\n```\n\nBut often your expectations occur in _pairs_: a \"before\" and an \"after\": one for the state of\nsomething _before_ the action and a matching expectation for the state of the same thing _after_\nthe action.\n\nAs the number of pairs grows, it can be quite hard for the reader of your test to see which\nexpectations are related, and how. It can also be hard for the writer of the test to maintain, esp.\nif they are relying on things like the order and proximity of the expectations alone to indicate a\nconnection between related before/after expectations.\n\nThe `make_changes` (and `before_and_after`) matchers provided by this library give you a tool to\nmake it explicit and _extremely_ clear that those 2 expectations are a pair that are very tightly\nrelated.\n\nSo instead of the above, you can rewrite it as explicit pairs like this:\n\n```ruby\n  expect {\n    perform_change\n  }.to make_changes([\n    -\u003e{ expect(thing.a).to eq 1 },\n    -\u003e{ expect(thing.a).to eq 0 },\n  ], [\n    -\u003e{ expect(thing.b).to eq 2 },\n    -\u003e{ expect(thing.b).to eq -2 },\n  ], [\n    -\u003e{ expect(thing.c).to eq 9 },\n    -\u003e{ expect(thing.c).to eq 9 },\n  ])\n```\n\nor, using `change` matchers, like this:\n\n```ruby\n  expect {\n    perform_change\n  }.to make_changes(\n    change { thing.a }.from(1).to(0),\n    change { thing.b }.from(2).to(-2),\n    change { thing.c }.from(3).to(9),\n  )\n```\n\n(or any combination of `[before_proc, after_proc]` arrays and `change` matchers)\n\nRSpec already provides a built-in matcher for expressing changes expected as a result of executing a\nblock. And it works great for specifying single changes. You can even use it for specifying multiple\nchanges:\n\n```ruby\n  expect {\n    expect {\n      expect {\n        perform_change\n      }.to change { thing.a }.from(1).to(0)\n    }.to change { thing.b }.from(2).to(-2)\n  }.to change { thing.c }.from(3).to(9)\n```\n\nGranted, that makes it _much_ clearer at a quick glance what changes are expected by your action.\nBut it also has some drawbacks:\n\n1. You have to completely rewrite your expectations — which may have started out (as in our example)\n   as good old plain `expect(something).to eq something` expectations — into a _very_ different\n   `change()` matcher style.\n2. You end up with extra nesting, which can make your tests look a bit unwieldy and harder to read\n   than it needs to be.\n\nRSpec provides a pretty good solution to the nesting problem via its compound (`and`/`\u0026`) matchers\nthat let you combine several matchers together and treat them as one, so you can actually simplify\nthat to just a single event block using just built-in RSpec:\n```ruby\n  expect {\n    perform_change\n  }.to (\n    change { thing.a }.from(1).to(0) \u0026\n    change { thing.b }.from(2).to(-2) \u0026\n    change { thing.c }.from(3).to(9)\n  )\n```\n\n`change` is often all you need for changes to primitive values. But it isn't always enough for\ndoing more complex before/after checks. And it's not always convenient to rewrite existing\nexpectations to a different (`change`) syntax.\n\n`make_changes` gives you the flexibility to either leave your before/after expectations as \"regular\"\n`expect`s or use the `change()` matcher style of expecting changes.\n\nThis flexibility gives you the power and flexibility to lets you express some things that you simply\ncouldn't express if you were limited to only the `change` matcher, such as things that you would\nnormally use another specialized matcher for, such as expectations on arrays or hashes:\n\n```ruby\n  expect {\n    perform_change\n  }.to make_changes([\n    -\u003e{ expect_too_close_to_pedestrians(car) },\n    -\u003e{ expect_sufficient_proximity_from_pedestrians(car) },\n  ], [\n    -\u003e{ expect(instance.tags).to match_array [:old_tag] },\n    -\u003e{ expect(instance.tags).to match_array [:new_tag] },\n  ], [\n    -\u003e{\n      expect(team.members).to     include user_1\n      expect(team.members).to_not include user_2\n    },\n    -\u003e{ expect(team.members).to include user_1, user_2 },\n  ])\n```\n\nAlthough you _can_ define aliases and negated matchers to do things like this:\n\n```ruby\nRSpec::Matchers.alias_matcher          :an_array_including, :include\nRSpec::Matchers.define_negated_matcher :an_array_excluding, :include\n\n      array = ['food', 'water']\n      expect { array \u003c\u003c 'spinach' }.to change { array }\n        .from( an_array_excluding('spinach') )\n        .to(   an_array_including('spinach') )\n```\n\n, it may not _always_ be possible, and requires you to define the needed matchers. Whereas\n`make_changes` allows you to simply use the \"regular\" matchers that you already know and love:\n\n```ruby\n    array = ['food', 'water']\n    expect { array \u003c\u003c 'spinach' }.to make_changes([\n      -\u003e{ expect(array).not_to include 'spinach' },\n      -\u003e{ expect(array).to     include 'spinach' },\n    ])\n```\n\nIt can sometimes be more readable or maintainable if you can just use/reuse regular expectations for\nyour before/after checks.\n\nYou might not even be checking for a change. You might simply want to assert that some invariant\nstill holds both before _and_ after your action is performed.\n\n```ruby\n  expect {\n    perform_change\n  }.to check_all_before_and_after([\n    -\u003e{ expect(car).to_not be_too_close },\n    -\u003e{ expect(car).to_not be_too_close },\n  ])\n```\n\nThat would be difficult or impossible to express using `change` matchers, since it's not a change.\n\n\n## `before_and_after` matcher\n\nIn the examples above, if you pass an array to `make_changes` as one of the \"expected changes\", it\nactually converts that to a `before_and_after` matcher, and then `and`s together all of the\n\"expected changes\" into a single `Compound::And` matcher.\n\nIf you wanted to, you can also use `before_and_after` directly, like:\n\n```ruby\n    expect { @instance.some_value = 6 }.to before_and_after(\n      -\u003e { expect(@instance.some_value).to eq 5 },\n      -\u003e { expect(@instance.some_value).to eq 6 }\n    )\n```\n\n\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'rspec-make_changes'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install rspec-make_changes\n\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/TylerRick/rspec-make_changes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftylerrick%2Frspec-expect_to_make_changes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftylerrick%2Frspec-expect_to_make_changes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftylerrick%2Frspec-expect_to_make_changes/lists"}