Giter Site home page Giter Site logo

clarkedb / grift Goto Github PK

View Code? Open in Web Editor NEW
9.0 1.0 0.0 303 KB

Mocking and spying in Ruby's MiniTest framework

Home Page: https://clarkedb.github.io/grift

License: MIT License

Ruby 99.84% Shell 0.16%
ruby minitest minitest-plugins mock mocking unit-testing testing testing-tools

grift's Introduction

Grift

gem version build codecov

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. 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

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 mock on your mock method object.

# get the number of times the mocked method has been called
my_mock.mock.count
#=> 2

# get args for each call to the method while mocked
my_mock.mock.calls[0].args
#=> ['first_arg1', 'second_arg1']

# get kwargs for each call to the method while mocked
my_mock.mock.calls[0].kwargs
#=> { first_arg1: 'value' }

# get results (return value) for each call to the method while mocked
my_mock.mock.results
#=> ['result1', 'result2']

Requirements

Grift supports all Ruby versions >= 2.7 (including 3.3).

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 . For those with write access: 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.

Docs

The docs are generated using YARD. To build the docs, first gem install yard . Then run yardoc to build the new docs. This is always done before a release to update the docs that get published and made available with the gem.

Contributing

Bug reports and pull requests are welcome on GitHub at clarkedb/grift. Before submitting a pull request, see CONTRIBUTING.

grift's People

Contributors

clarkedb avatar dependabot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

grift's Issues

Automatically publish gem on release

This happens manually right now with a rake command. We could use GitHub actions:

name: Publish

on:
  release:
    types: [published]

jobs:
  build:
    name: Publish to Rubygems
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: Set up Ruby 2.7
        uses: actions/setup-ruby@v1
        with:
          ruby-version: 2.7

      - name: Publish to RubyGems
        uses: dawidd6/action-publish-gem@v1
        with:
          api_key: ${{secrets.RUBYGEMS_AUTH_TOKEN}}

Unclear how to access keyword args in mock results

Right now it seems you can just do .mock.calls[0].last[:some_keyword] but it isn't documented anywhere. Also that makes it seem like this isn't actually compatibile with Ruby 3.0 where the kwargs as the last arg is deprecated.

We should add tests, ensure support, and document how to access keyword arguments.

Mocked inherited methods

Inherited methods can be mocked as of #34, but after cleanup they are defined by the child class and are no longer purely inherited.

For example this test would fail on the last line:

class TargetTwo < Target; end

def test_it_mocks_inherited_methods
    target_two = TargetTwo.new(first_name: 'Charles', last_name: 'Boyle')
    refute TargetTwo.method_defined?(:full_name, false)
    assert TargetTwo.method_defined?(:full_name, true)
    full_name_mock = Grift::MockMethod.new(TargetTwo, :full_name).mock_return_value('Jake Peralta')
    assert TargetTwo.method_defined?(:full_name, false)
    assert_equal 'Jake Peralta', target_two.full_name
    full_name_mock.mock_restore
    refute TargetTwo.method_defined?(:full_name, false)
end

LocalJumpError when no block passed to mock_implementation

If you fail to pass a block to Grift::MockMethod#mock_implementation then when the mocked method gets executed, it raises a LocalJumpError because there is no block passed to yield.

For example

Grift.spy_on(String, :upcase).mock_implementation
"banana".upcase

would produce

LocalJumpError: no block given (yield)
    /.../grift/mock_method.rb:144:in `block in mock_implementation'

Grift Version: 2.0.0
Ruby Version: 3.0.3

Spying on a method that takes a block prevents the block from being passed

Suppose we have a method that takes a block like Thread.new. Then if we try to mock that method, the block doesn't get passed. Consider the following example

# target code
def self.some_method
  puts 'hi'
  Thread.new { puts 'hello' }
end

# test code
def test_some_method
  thread_spy = Grift.spy_on(Thread, :new)
  assert_output(/hello/) do
     MyClass.some_method
     thread_spy.mock.results.first.join # attempt to force the thread block to execute
  end
end

This fails with an error ThreadError: must be called with a block which signals that Grift is failing to pass on the block while spying on Thread.new.

This can probably be fixed by augmenting |*args, **kwargs| to include |*args, **kwargs, &block|.

Grift can't mock constructor

Grift Version: 1.0.1

Steps to reproduce:

Grift.mock(String, :new)

Description

It looks like it might also fail for create in Rails's ActiveRecord::Model. Or maybe all super methods?

Add custom mock assertions

Could add minitest assertions used with objects

  • assert_mocked
  • assert_mock_called
  • assert_mock_called_with (maybe)

Add restricted methods and restrict duplicate mocking

Prevent Grift from mocking anything internal, or any internal dependencies.

This includes all Grift, Grift:: and Minitest classes/modules.

Also String :to_sym or Array :map :push :count :empty? or Hash :[] Class :singleton_class :instance_methods :remove_method :define_method :alias_method :send

Add yard documention

Should add documentation for any undocumented files and build the documentation.
Look into automatically publishing these docs to a github page (maybe using jekyll?)

Look into using yard-junk as well adding to lint workflow:

      - name: Yard-Junk
        run: bundle exec yard-junk --path lib

Try installing and using built gem in CI

We should make sure we add another action that builds the gem with rake build and then grabs the gem build from pkg/grift-x.x.x.gem and tries to install it locally with a gem install --local <path> and then use it.

Mocking returns file load error from the Grift::Config

Grift Version : 1.0.0

Steps to reproduce

Grift.mock(String, :upcase)

When mocking it throws an error trying to check the config because it can't find lib/grift/config/restricted.yml. This is probably due to a relative path issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.