私のテストでは、クラスの任意のインスタンスの定型応答をスタブ化します。
次のようになります。
Book.stubs(:title).any_instance().returns("War and Peace")
その後、@book.title
を呼び出すたびに、「戦争と平和」を返します。
MiniTest内でこれを行う方法はありますか?はいの場合、サンプルコードを教えてください。
または、モカのようなものが必要ですか?
MiniTestはモックをサポートしますが、モックは私が必要とするものに対してはやり過ぎです。
# 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
モックライブラリのない単純なスタブに興味がある場合は、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
私はすべてのGemsテストにミニテストを使用しますが、すべてのスタブをモカで実行します。すべてをモックでミニテストで実行できる場合があります(スタブなどはありませんが、モックは非常に強力です)。それが役立つ場合、素晴らしい仕事:
require 'mocha'
Books.any_instance.stubs(:title).returns("War and Peace")
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
これは本当にばかげた例ですが、少なくともあなたがやりたいことをする方法についての手がかりを与えてくれます。 MiniTest v2.5.1を使用してこれを試しました。これはRuby 1.9に付属するバンドルバージョンで、このバージョンでは#stubメソッドがまだサポートされていないようですが、 MiniTest v3.で試してみましたが、それは魅力的でした。
MiniTest!をご利用いただきありがとうございます。
編集:これには別のアプローチもあり、少しハックされているように見えますが、それでもあなたの問題の解決策です:
klass = Class.new Book do
define_method(:title) { "War and Peace" }
end
klass.new.title.must_equal "War and Peace"
Minitestでこれを行うことはできません。ただし、特定のインスタンスをスタブ化できます。
book = Book.new
book.stub(:title, 'War and Peace') do
assert_equal 'War and Peace', book.title
end
@ 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
_を呼び出した場合のみです。
テストコードでモジュールをいつでも作成でき、それを含むモンキーパッチクラスまたはオブジェクトにincludeまたはextendを使用できます。例(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
これにより、単純なスタブが許可するよりも複雑な動作をまとめることができます
ここで答えに基づいて構築した例を共有すると思いました。
メソッドの長いチェーンの最後にメソッドをスタブする必要がありました。それはすべて、Paypal APIラッパーの新しいインスタンスから始まりました。スタブするために必要な呼び出しは、本質的に次のとおりです。
_Paypal_api = Paypal::API.new
response = Paypal_api.make_payment
response.entries[0].details.payment.amount
_
メソッドがamount
でない限り、自分自身を返すクラスを作成しました。
_Paypal_api = Class.new.tap do |c|
def c.method_missing(method, *_)
method == :amount ? 1.25 : self
end
end
_
次に、それを_Paypal::API
_にスタブしました:
_Paypal::API.stub :new, Paypal_api do
get '/Paypal_payment', amount: 1.25
assert_equal 1.25, payments.last.amount
end
_
ハッシュを作成してhash.key?(method) ? hash[method] : self
を返すことにより、複数のメソッドでこの機能を使用できます。