Grift
Mocking and spying in Ruby's MiniTest framework
Installation
Add this line to your application's Gemfile:
gem 'grift'
And then execute:
bundle install
Or install it yourself as:
gem install grift
MiniTest Plugin
We recommend using the plugin so that mocks are cleaned up after each test automatically. The plugin also adds assertions for mocks. To enable the plugin, add the following lines of code to your test_helper file.
class Minitest::Test
include Grift::MinitestPlugin
end
Or for Ruby on Rails:
class ActiveSupport::TestCase
include Grift::MinitestPlugin
end
Usage
For complete usage guide, see the docs.
Spy
To "mock" a method and spy on its call args and results without changing the behavior of the method:
my_mock = Grift.spy_on(MyClass, :my_method)
Mock
To mock a method and its return value:
my_mock = Grift.mock(MyClass, :my_method, return_value)
my_spy = Grift.spy_on(MyClass, :my_method)
my_spy.mock_return_value(return_value)
To mock the implementation:
my_spy = Grift.spy_on(MyClass, :my_method)
my_spy.mock_implementation do |arg1, arg2|
x = do_something(arg1, arg2)
do_something_else(x) # the last line will be returned
end
or for a method taking keyword arguments:
my_spy = Grift.spy_on(MyClass, :my_method)
my_spy.mock_implementation do |arg1, arg2, **kwargs|
x = do_something(arg1, arg2, kwargs[:arg3], kwargs[:arg4])
do_something_else(x) # the last line will be returned
end
Class and instance methods with the same name
When a class responds to a method itself, Grift mocks the class method. That includes methods every class has, like name, to_s, and ==. To mock the instance method instead, pass instance: true to spy_on:
my_mock = Grift.spy_on(MyClass, :name, instance: true).mock_return_value('mocked')
Chaining
You can chain mock_return_value and mock_implementation after initializing the mock.
my_mock = Grift.spy_on(MyClass, :my_method).mock_implementation do |*args, **kwargs|
do_something(*args, **kwargs)
end
#=> Grift::MockMethod object is returned
Results
To get the results and details of the calls, call calls or results on your mock method object.
# get the number of times the mocked method has been called
my_mock.count
#=> 2
# get args for each call to the method while mocked
my_mock.calls[0].args
#=> ['first_arg1', 'second_arg1']
# get kwargs for each call to the method while mocked
my_mock.calls[0].kwargs
#=> { first_arg1: 'value' }
# get results (return value) for each call to the method while mocked
my_mock.results
#=> ['result1', 'result2']
Assertions
With the MiniTest Plugin included, you can assert on mocks directly in your tests.
assert_mocked MyClass, :my_method
refute_mocked MyClass, :other_method
assert_mock_called my_mock
refute_mock_called my_mock
# passes if any call used exactly these arguments
assert_mock_called_with my_mock, 'first_arg', key: 'value'
refute_mock_called_with my_mock, 'other_arg'
When an assertion fails, the message lists the calls to the mocked method.
Requirements
Grift supports all Ruby versions >= 2.7 (including 4.x versions).
Support
For now Grift is not guaranteed to be compatible with Ruby::Box introduced in Ruby 4.0.0. Support will be added/guaranteed if it is determined that Ruby::Box will move out of the experimental phase.
Development
After forking the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
When developing, to install Grift whith your changes onto your local machine, run bundle exec rake install .
Releasing
Releases are managed by release-please. When releasable commits (feat, fix, perf, or breaking changes; see CONTRIBUTING) land on main, it opens (or updates) a release pull request that bumps lib/grift/version.rb and CHANGELOG.md. For those with write access:
- Merge the release pull request, which tags the version and creates the GitHub release
- Once CI passes on
main, approve thereleasedeployment in GitHub Actions, which pushes the.gemfile to rubygems.org via trusted publishing and then deploys the docs
If publishing fails, use "Re-run failed jobs" on the workflow run. "Re-run all jobs" re-runs release-please, which finds the release already exists and skips publishing.
Docs
The docs are generated using YARD and deployed to GitHub Pages after each release. To redeploy them, run the Docs workflow manually with the latest release tag. To build them locally, run BUNDLE_WITH=docs bundle install and then bundle exec yard (output goes to the gitignored docs/ folder).
Contributing
Bug reports and pull requests are welcome on GitHub at clarkedb/grift. Before submitting a pull request, see CONTRIBUTING.