Stubberry

Pay attention it has 2Bs and 2Rs in naming :)

This gem is planned to be an ultimate sweet collection of stubbing methods for any kinds of testing. Any new cool stubbing suggestions are welcome.

Installation

Add this line to your application's Gemfile:

# stubberry extends Object methods, so it should not be required in production
group :test, :development do
    gem 'stubberry'
end

#OR 
gem 'stubberry', group: [:test, :development]

And then execute:

$ bundle install

Or install it yourself as:

$ gem install stubberry

Usage

Version 0.1 of this gem has two cool sets of stub methods Stubberry::Object and Stubberry::ActiveRecord

Stubberry::Object

Set of stubbing methods added to an Object class hence available for any class or its instances.


# stub_must( name, val_or_callable, *block_args )
# raise error functionality whenever stubbed method wasn't called

test 'check mehtod "run" params and execution' do 
  class_or_obj.stub_must(:run, -> ( param ) {
    # Now you can be sure that execution was here and either you have an expected param, OR 
    # if call didn't happened you will see a StandardError raised 
    assert_equal( param, 1 )
  } ) { class_or_obj.run(1) }
end

Rem: I know about Mock object, but I don't like it. You may consider approach with mock/verify better or more object oriented e.t.c. But I do like an error to be aligned to the check, running mock.verify some place after check did happened, feels unnatural and uncomfortable to me


# stub_must_not( name, message = nil ) 
# the reverse method of stub_must -- will raise an issue whenever method
# **was** called inside a stubbing block, ensures that flow didn't reach given method 
test 'call must not happened' do
  # nothing raised
  class_or_obj.stub_must_not(:run) { class_or_obj.call(1) }
  # StandardError will be raised: 
  class_or_obj.stub_must_not(:run) { class_or_obj.run(1) }
end

# stub_must_all( name_to_var_or_callable, &block )
# just for fun multiple stub_must in one call, will raise one error for any amount of missing method's calls
test 'all calls should happened' do
  class_or_obj.stub_must_all(
    run: true,
    call: -> (param) { assert_equal(param, 1) }
  ) do 
    class_or_obj.call(1)
    class_or_obj.run(args)
  end
end

# stub_if_def(name, val_or_callable, *block_args, &block)
# stub only if method present on the object, otherwise just execute block. 
# It's a really rare case, I used only once for incompatible gems versions test. 
# When I needed to check that the execution flow can survive missing methods. 
#
test 'all calls should happened' do
  class_or_obj.stub_if_def( :not_def, -> (param) { assert_equal(param, :param) } ) do
    # when there is nothing to stub, just yield
    :just_yield_this_without_stubbing
  end
end

# stub_must_if_def(name, val_or_callable, *block_args, &block)
# same as above but with stub_must under the hood

Stubberry::ActiveRecord

Easy stubbing for ActiveRecord objects, you just need the id and Stubberry will do the rest. The cool stuff about these stubbing methods is an independence from the way object retrieved. You can get object via find, where or any relation, it doesn't matter. Stubbing is based on after_find callback, so whenever object instantiated it will be properly stubbed.

# you can stub active record object attributes with stub_orm_attr

test 'object attributes will be stubbed in relations' do 
  Comment.stub_orm_attr(1, {body: 'ola!'} ) do 
    assert_equal( 'ola!', User.first.comments.where(id: 1).take.body )
    assert_equal( 'ola!', Comment.find(1).body )
  end
end

# you can stub active record object method with stub_orm_method
test 'object with a given id got method stubbed' do
  Comment.stub_orm_method(1, join: -> ( other ) {
    assert_equal( other.id, 2 )
  } ) do
    User.first.comments.where(id: 1).each{ _1.join( Comment.find(2) ) }
  end
end

Stubberry::Assertions

Control flow without interference, you want to be sure that method called and stubbing either to verbose or problematic.


class YouTest < Minitest::TestCase

    include Stubberry::Assertions

    test 'save called without object interference' do 
      cmnt = Comment.new
      assert_difference( -> { Comment.count } ) do 
          assert_method_called( cmnt, :save ) { cmnt.save }
      end
    end

    test 'gently check method params' do
      cmnt = Comment.new
      assert_method_called( cmnt, :save, -> (options) {
        assert_equal( options, {validate: false} )
      } ) { cmnt.save(validate: false) }
    end
end

Development

After checking out 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.

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

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/alekseyl/stubberry. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the Stubberry project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.