How can I drown things in MiniTest?

As part of my test, I want to drown out the complete answer for any instance of the class.

It might look something like this:

Book.stubs(:title).any_instance().returns("War and Peace") 

Then when I call @book.title , it returns War and Peace.

Is there a way to do this in MiniTest? If so, can you give me an example code snippet?

Or do I need something like mocha?

MiniTest supports Mocks, but Mocks is overkill for what I need.

+56
ruby stub minitest
Aug 26 '11 at 10:15
source share
8 answers
  # Create a mock object book = MiniTest::Mock.new # Set the mock to expect :title, return "War and Piece" # (note that unless we call book.verify, minitest will # not check that :title was called) book.expect :title, "War and Piece" # Stub Book.new to return the mock object # (only within the scope of the block) Book.stub :new, book do wp = Book.new # returns the mock object wp.title # => "War and Piece" end 
+31
Jan 07 '13 at 21:46
source share

If you are interested in simply trimming without a mocking library, then it's easy enough to do it in Ruby:

 class Book def avg_word_count_per_page arr = word_counts_per_page sum = arr.inject(0) { |s,n| s += n } len = arr.size sum.to_f / len end def word_counts_per_page # ... perhaps this is super time-consuming ... end end describe Book do describe '#avg_word_count_per_page' do it "returns the right thing" do book = Book.new # a stub is just a redefinition of the method, nothing more def book.word_counts_per_page; [1, 3, 5, 4, 8]; end book.avg_word_count_per_page.must_equal 4.2 end end end 

If you need something more complex, for example, truncating all instances of a class, then this is also quite easy to do, you just need to create an ad a bit:

 class Book def self.find_all_short_and_unread repo = BookRepository.new repo.find_all_short_and_unread end end describe Book do describe '.find_all_short_unread' do before do # exploit Ruby constant lookup mechanism # when BookRepository is referenced in Book.find_all_short_and_unread # then this class will be used instead of the real BookRepository Book.send(:const_set, BookRepository, fake_book_repository_class) end after do # clean up after ourselves so future tests will not be affected Book.send(:remove_const, :BookRepository) end let(:fake_book_repository_class) do Class.new(BookRepository) end it "returns the right thing" do # Stub #initialize instead of .new so we have access to the # BookRepository instance fake_book_repository_class.send(:define_method, :initialize) do super def self.find_all_short_and_unread; [:book1, :book2]; end end Book.find_all_short_and_unread.must_equal [:book1, :book2] end end end 
+26
Apr 26 2018-12-12T00:
source share

I use minitest to test my Gems, but all my stubs with mocha can probably be done in a mini-series using Mocks (there are no stubs or anything else, but the layouts are pretty powerful), but I find moka to do fine, if this helps:

 require 'mocha' Books.any_instance.stubs(:title).returns("War and Peace") 
+21
Aug 28 '11 at 7:18
source share

You can easily drown methods in MiniTest . Information is available at github .

So, following your example and using the Minitest::Spec style, here's how you should stub methods:

 # - RSpec - Book.stubs(:title).any_instance.returns("War and Peace") # - MiniTest - # Book.stub :title, "War and Peace" do book = Book.new book.title.must_equal "War and Peace" end 

This is a really stupid example, but at least it gives you an idea of ​​how to do what you want to do. I tried this using MiniTest v2.5.1, which is the bundled version that comes with Ruby 1.9, and it looks like the #stub method was not yet supported in this version, but then I tried with MiniTest v3.0, and it worked like charm.

Good luck and congratulations using MiniTest!

Edit: There is another approach for this, and although it seems a bit hacky, it is still a solution to your problem:

 klass = Class.new Book do define_method(:title) { "War and Peace" } end klass.new.title.must_equal "War and Peace" 
+18
May 24 '12 at 16:05
source share

You cannot do this with Minitest. However, you can drown out a particular instance:

 book = Book.new book.stub(:title, 'War and Peace') do assert_equal 'War and Peace', book.title end 
+13
Mar 13 '15 at 10:12
source share

Just to further explain @panic's answer , suppose you have a Book class:

 require 'minitest/mock' class Book; end 

First, create a stub for the Book instance and make it return the desired title (any number of times):

 book_instance_stub = Minitest::Mock.new def book_instance_stub.title desired_title = 'War and Peace' return_value = desired_title return_value end 

Then make the Book class instantiate your Book instance (only and always, in the following block of code):

 method_to_redefine = :new return_value = book_instance_stub Book.stub method_to_redefine, return_value do ... 

Inside this block of code (only), the Book::new method is a stub. Let's try this:

  ... some_book = Book.new another_book = Book.new puts some_book.title #=> "War and Peace" end 

Or, briefly:

 require 'minitest/mock' class Book; end instance = Minitest::Mock.new def instance.title() 'War and Peace' end Book.stub :new, instance do book = Book.new another_book = Book.new puts book.title #=> "War and Peace" end 

Alternatively, you can set the extension gem minitest-stub_any_instance . (Note: when using this approach, the Book#title method must exist before you drown it.) Now you can say more simply:

 require 'minitest/stub_any_instance' class Book; def title() end end desired_title = 'War and Peace' Book.stub_any_instance :title, desired_title do book = Book.new another_book = Book.new puts book.title #=> "War and Peace" end 

If you want to make sure that Book#title is called a certain number of times, do:

 require 'minitest/mock' class Book; end book_instance_stub = Minitest::Mock.new method = :title desired_title = 'War and Peace' return_value = desired_title number_of_title_invocations = 2 number_of_title_invocations.times do book_instance_stub.expect method, return_value end method_to_redefine = :new return_value = book_instance_stub Book.stub method_to_redefine, return_value do some_book = Book.new puts some_book.title #=> "War and Peace" # And again: puts some_book.title #=> "War and Peace" end book_instance_stub.verify 

Thus, for any particular instance, a call to the MockExpectationError: No more expects available method MockExpectationError: No more expects available than specified, calls MockExpectationError: No more expects available .

In addition, for any particular instance, a call to the MockExpectationError: expected title() method fewer times than indicated calls MockExpectationError: expected title() , but only if you call #verify for that instance at this point.

+12
Jun 23 '16 at 12:04 on
source share

You can always create a module in your test code and use include or extension for classes or monkey-patch objects with it. e.g. (in book_test.rb)

 module BookStub def title "War and Peace" end end 

Now you can use it in your tests

 describe 'Book' do #change title for all books before do Book.include BookStub end end #or use it in an individual instance it 'must be War and Peace' do b=Book.new b.extend BookStub b.title.must_equal 'War and Peace' end 

This allows you to compose more complex behavior than a simple stub.

+5
Jul 28 '15 at 9:39 on
source share

I thought I would share an example that I built on the answers here.

I needed to stub the method at the end of a long chain of methods. It all started with a new instance of the PayPal API shell. The call I needed was essentially:

 paypal_api = PayPal::API.new response = paypal_api.make_payment response.entries[0].details.payment.amount 

I created a class that returned itself if the method was not amount :

 paypal_api = Class.new.tap do |c| def c.method_missing(method, *_) method == :amount ? 1.25 : self end end 

Then I overlaid it on the PayPal::API :

 PayPal::API.stub :new, paypal_api do get '/paypal_payment', amount: 1.25 assert_equal 1.25, payments.last.amount end 

Can you do this job with more than one method by making a hash and returning hash.key?(method) ? hash[method] : self hash.key?(method) ? hash[method] : self .

+1
Mar 11 '16 at 21:40
source share



All Articles