65

テスト内で、クラスの任意のインスタンスに対する定型応答をスタブしたいと考えています。

次のようになります。

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

その後、呼び出すたびに@book.title「戦争と平和」が返されます。

MiniTest 内でこれを行う方法はありますか? はいの場合、コード スニペットの例を教えていただけますか?

それともモカのようなものが必要ですか?

MiniTest はモックをサポートしていますが、モックは私が必要としているものには過剰です。

4

8 に答える 8

43
  # 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
于 2013-01-07T21:46:56.653 に答える
28

私はすべての Gem のテストに minitest を使用していますが、すべてのスタブを mocha で実行しています。すべてを minitest で Mocks を使用して実行できる可能性があります (スタブなどはありませんが、モックは非常に強力です)。それが役立つなら、素晴らしい仕事:

require 'mocha'    
Books.any_instance.stubs(:title).returns("War and Peace")
于 2011-08-28T07:18:09.537 に答える
26

モック ライブラリを使用しない単純なスタブに興味がある場合は、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

クラスのすべてのインスタンスをスタブ化するなど、より複雑なものが必要な場合も、簡単に実行できます。少し工夫するだけで済みます。

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's 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
于 2012-04-26T07:37:41.157 に答える
24

でクラス メソッドを簡単にスタブ化できますMiniTest。情報はgithubで入手できます。

したがって、あなたの例に従って、Minitest::Specスタイルを使用すると、これがメソッドをスタブ化する方法です。

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

これは非常にばかげた例ですが、少なくともやりたいことを実行する方法の手がかりが得られます。Ruby 1.9に付属のバンドル バージョンであるMiniTest v2.5.1を使用してこれを試してみましたが、このバージョンでは #stub メソッドはまだサポートされていないようでしたが、MiniTest v3.0で試してみたところ、うまくいきました。

MiniTestをご利用いただき、誠にありがとうございます。

編集:これには別のアプローチもあり、少しハックに見えますが、それでも問題の解決策です:

klass = Class.new Book do
  define_method(:title) { "War and Peace" }
end

klass.new.title.must_equal "War and Peace"
于 2012-05-24T16:05:41.087 に答える
19

@panic's answerをさらに説明するために、 Book クラスがあると仮定しましょう。

require 'minitest/mock'
class Book; end

まず、Book インスタンス スタブを作成し、目的のタイトルを (何度でも) 返すようにします。

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

次に、Book クラスが Book インスタンス スタブをインスタンス化するようにします (次のコード ブロック内のみ)。

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

このコード ブロック (のみ) 内で、Book::newメソッドがスタブ化されます。試してみよう:

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

または、最も簡潔に:

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

または、Minitest 拡張機能 gem をインストールすることもできますminitest-stub_any_instance。(注: このアプローチを使用するBook#title場合、スタブする前にメソッドが存在している必要があります。) ここで、より簡単に次のように言えます。

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

Book#titleが特定の回数呼び出されることを確認する場合は、次のようにします。

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

したがって、特定のインスタンスについて、スタブ化されたメソッドを指定された回数よりも多く呼び出すと、 が発生しMockExpectationError: No more expects availableます。

また、特定のインスタンスについて、スタブ化されたメソッドを指定された回数よりも少ない回数呼び出すと、その時点でそのインスタンスMockExpectationError: expected title()を呼び出した場合にのみ発生します。#verify

于 2016-06-23T12:04:07.707 に答える
5

テスト コードでいつでもモジュールを作成し、インクルードまたはエクステンドを使用して、モンキー パッチ クラスまたはオブジェクトを使用できます。例 (book_test.rb)

module BookStub
  def title
     "War and Peace"
  end
end

これで、テストで使用できます

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

これにより、単純なスタブよりも複雑な動作をまとめることができます

于 2015-07-28T09:39:42.290 に答える