Railsアプリケーションに取り組んでいます。メソッドをグローバルにスタブしようとしています。
私がやっていることは、RSpec構成内のbefore(:suite)
ブロックで次のようにスタブ化することです。
_RSpec.configure do |config|
config.before(:suite) do
allow_any_instance_of(MyModel).to receive(:my_method).and_return(false)
end
end
_
ただし、テストの開始は次のエラーで失敗します。
in `method_missing': undefined method `allow_any_instance_of' for #<RSpec::Core::ExampleGroup:0x00000008d6be08> (NoMethodError)
どんな手掛かり? RSpecを使用してメソッドをグローバルにスタブするにはどうすればよいですか?
P.
おそらくコンテキスト/初期化の問題です。 config.before(:each)
でこれを行うと、問題が解決するはずです。
rspec-mocks README に記載されているように、各例の後でスタブがクリアされるため、before(:suite)
でメソッドをスタブしないでください。
before(:each)
ではなくbefore(:all)
を使用してください
before(:all)
のスタブはサポートされていません。その理由は、各例の後ですべてのスタブとモックが消去されるため、before(:all)
に設定されているスタブは、そのグループで偶然実行される最初の例では機能しますが、他の例では機能しないためです。
before(:all)
の代わりにbefore(:each)
を使用してください。
_allow_any_instance_of
_がbefore(:suite)
ブロックでは使用できないが、before(:each)
ブロックでは使用できるのはそのためだと思います。
それでもメソッドが見つからない場合は、_:should
_構文のみを許可するようにrspec-mocksを構成した可能性があります。 _allow_any_instance_of
_はRSpec 2.14で導入され、メッセージを期待するためのすべての新しい_:expect
_構文を備えています。
_RSpec::Mocks.configuration.syntax
_の値を調べて、この構文が有効になっていることを確認してください。これは、rspec-mocksで使用可能な構文の配列です。使用可能な構文は_:expect
_および_:should
_です。
_RSpec.configure do |config|
config.mock_with :rspec do |mocks|
mocks.syntax = [:expect, :should]
end
end
_
適切に構成すると、_allow_any_instance_of
_を使用できるようになります。
最近、before(:all)
またはbefore(:context)
ブロックで何かをスタブする必要がある場合に遭遇し、ここでの解決策が私のユースケースでは機能しないことがわかりました。
before()&after()フック のRSpecドキュメントはサポートされていないと言っています:
beforeフックとafterフックは、それらを実行する必要のあるサンプルグループ、またはグローバルRSpec.configureブロックで直接定義できます。
警告:インスタンス変数の設定はbefore(:suite)ではサポートされていません。
警告:モックはbefore(:example)でのみサポートされています
注::exampleおよび:contextスコープは、それぞれ:eachおよび:allとしても使用できます。どちらでもお使いください。
私は バイナリファイル形式を書き込むための宝石 を作成しました。これは、バイナリヘッダー内のUNIX Epochタイムスタンプに含まれていました。 RSpecテストを記述して、出力ファイルのヘッダーが正しいかどうかを確認し、それをテストフィクスチャのバイナリ参照ファイルと比較したいと思いました。高速なテストを作成するために、すべてのサンプルグループブロックが実行される前に、ファイルを一度書き出す必要がありました。タイムスタンプを参照ファイルと照合するために、Time.now()
が定数値を返すように強制する必要がありました。これにより、目的の値を返すために_Time.now
_をスタブ化しようとする道が進みました。
ただし、_rspec/mocks
_はbefore(:all)
またはbefore(:context)
ブロック内でのスタブをサポートしていないため、機能しませんでした。ファイルbefore(:each)
を書き込むと、他の奇妙な問題が発生しました。
幸いにも、私は偶然出会いました rspec-mocksの問題#24 解決策がありました!
2014年1月9日以降( rspec-mocks PR#519 )RSpecには、これを回避するメソッドが含まれています。
_RSpec::Mocks.with_temporary_scope
_
_require 'spec_helper'
require 'rspec/mocks'
describe 'LZOP::File' do
before(:all) {
@expected_lzop_magic = [ 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a ]
@uncompressed_file_data = "Hello World\n" * 100
@filename = 'lzoptest.lzo'
@test_fixture_path = File.join(File.dirname(__FILE__), '..', 'fixtures', @filename + '.3')
@lzop_test_fixture_file_data = File.open( @test_fixture_path, 'rb').read
@tmp_filename = File.basename(@filename)
@tmp_file_path = File.join( '', 'tmp', @tmp_filename)
# Stub calls to Time.now() with our fake mtime value so the mtime_low test against our test fixture works
# This is the mtime for when the original uncompressed test fixture file was created
@time_now = Time.at(0x544abd86)
}
context 'when given a filename, no options and writing uncompressed test data' do
describe 'the output binary file' do
before(:all) {
RSpec::Mocks.with_temporary_scope do
allow(Time).to receive(:now).and_return(@time_now)
# puts "TIME IS: #{Time.now}"
# puts "TIME IS: #{Time.now.to_i}"
my_test_file = LZOP::File.new( @tmp_file_path )
my_test_file.write( @uncompressed_file_data )
@test_file_data = File.open( @tmp_file_path, 'rb').read
end
}
it 'has the correct magic bits' do
expect( @test_file_data[0..8].unpack('C*') ).to eq @expected_lzop_magic
end
## [...SNIP...] (Other example blocks here)
it 'has the original file mtime in LZO file header' do
# puts "time_now= #{@time_now}"
if @test_file_data[17..21].unpack('L>').first & LZOP::F_H_FILTER == 0
mtime_low_start_byte=25
mtime_low_end_byte=28
mtime_high_start_byte=29
mtime_high_end_byte=32
else
mtime_low_start_byte=29
mtime_low_end_byte=32
mtime_high_start_byte=33
mtime_high_end_byte=36
end
# puts "start_byte: #{start_byte}"
# puts "end_byte: #{end_byte}"
# puts "mtime_low: #{@test_file_data[start_byte..end_byte].unpack('L>').first.to_s(16)}"
# puts "test mtime: #{@lzop_test_fixture_file_data[start_byte..end_byte].unpack('L>').first.to_s(16)}"
mtime_low = @test_file_data[mtime_low_start_byte..mtime_low_end_byte].unpack('L>').first
mtime_high = @test_file_data[mtime_high_start_byte..mtime_high_end_byte].unpack('L>').first
# The testing timestamp has no high bits, so this test should pass:
expect(mtime_low).to eq @time_now.to_i
expect(mtime_high).to eq 0
expect(mtime_low).to eq @lzop_test_fixture_file_data[mtime_low_start_byte..mtime_low_end_byte].unpack('L>').first
expect(mtime_high).to eq @lzop_test_fixture_file_data[mtime_high_start_byte..mtime_high_end_byte].unpack('L>').first
mtime_fixed = ( mtime_high << 16 << 16 ) | mtime_low
# puts "mtime_fixed: #{mtime_fixed}"
# puts "mtime_fixed: #{mtime_fixed.to_s(16)}"
expect(mtime_fixed).to eq @time_now.to_i
end
end
end
end
_
特定のメソッドがテストスイート全体に対して特定の動作をするようにしたい場合は、RSpecのスタブを処理する理由すらありません。代わりに、メソッドを単純に(再)定義して、テスト環境での動作を指定できます。
class MyModel
def my_method
false
end
end
これはspec/spec_helper.rb
または同様のファイル。
RSpecのどのバージョンを使用していますか?私は信じている allow_any_instance_of
はRSpec 2.14で導入されました。以前のバージョンでは、以下を使用できます。
MyModel.any_instance.stub(:my_method).and_return(false)
以下を使用して、クラス 'Xyz'のメソッド 'do_this'をスタブ化できます。
allow_any_instance_of(Xyz).to receive(:do_this).and_return(:this_is_your_stubbed_output)
これにより、この関数が呼び出された場所から-:: this_is_your_stubbed_outputに出力がスタブされます。
上記のコードをbefore(:each)ブロックで使用して、仕様のすべての例にこれを適用できます。