RSpec::ChangeToNow rdoc

RSpec::ChangeTo adds the to_now
and not_to_now
methods to change
matcher to describe how executing a block should change a matcher expectation.
Usage
Use the to_now
and not_to_now
(or not_to
, for short) methods to make assertions about the effect of an rspec change
block:
expect { @x = 1 }.to change { @x }.to_now eq 1
or
expect { @x = 1 }.to change { @x }.not_to eq 2
The method to_now
will check both that the matcher does not match prior to the change and that it does match after the change. The method not_to_now
(not_to
for short) will do the opposite, ensuring that the matcher matches prior to the change, and fails only after the change. All methods will ensure that a change actually takes place.
Also supported are aliases for those who don't want to split their infinitives and for those who would like to differently split them:
to_now
can also be called asnow_to
not_to_now
can also be callednot_to
,to_not
,to_not_now
andnot_now_to
Installation
Add this line to your application's Gemfile:
gem 'rspec-change_to_now'
And then execute:
$ bundle
Or install it yourself as:
$ gem install rspec-change_to_now
And require it as:
require 'rspec/change_to_now'
Why is this useful?
change { }.from().to()
adds expectation of pre- and post-conditions for a change, but it is restricted only to object values. With to_now
, you can write
list = []
expect { list << :a }.to change { list }.to_now include :a
whereas previously you would have to fully specify the original and final values of the list:
list = []
expect { list << :a }.to change { list }.from([]).to([:a])
While that may not seem like a big deal, the real values comes in more complex statements like:
person = Person.create(name: 'Taylor')
expect { person.siblings.create(name: 'Sam') }.to change { Person.all.map(&:name) }.to_now include('Taylor')
Arguably, I should be injecting some dependencies here instead of relying on globals, but Rails code doesn't always look like that. I'm looking forward to playing around with this and seeing if it really helps simplify specs. I'd love to hear your feedback.
Additional Matchers Provided
This gem also provides some additional matchers as detailed below.
negate(&block)
This gem also introduces the negate
matcher, which negates an existing matcher. You can use it like so:
expect(1).to negate(ne(1))
While it doesn't read every well, it serves an internal purpose, allowing a very simple implementation of to_now
using composable matcher inputs to the from
and to
methods as added in rspec 3.0.
detect(&block)
This gem also adds the detect
matcher, which behaves like the include
matcher when passed a satisfy
matcher created using the given block. You can use it like so:
list = []
expect { list << 2 }.to change { list }.to detect(&:even?)
This is the same as:
list = []
expect { list << 2 }.to change { list }.to include satisfy(&:even?)
A more interesting use might be:
person = Person.create(name: 'Taylor')
expect { person.siblings.create(name: 'Sam') }.to change {
Person.all
}.to_now detect { |person|
person.name == 'Taylor'
}
detect
behaves exactly like include
when it is not passed a block and will raise an exception if passed both expected items/matchers and a block.
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request