Подтвердить что ты не робот

Как я должен заглушить метод глобально с помощью RSpec?

Я работаю над приложением 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?

Р.

4b9b3361

Ответ 1

Вероятно, это проблема контекста/инициализации. Выполнение этой задачи в config.before(:each) должно решить вашу проблему.

Ответ 2

Не останавливайте методы в before(:suite), потому что после каждого примера очищаются заглушки, как указано в rspec-mocks README:

Используйте before(:each), а не before(:all)

Опоры в before(:all) не поддерживаются. Причина в том, что все заглушки и макеты очищаются после каждого примера, так что любой заглушка, которая установлена в before(:all) будет работать в первом примере, который запускается в этой группы, но не для других.

Вместо before(:all) используйте before(:each).

Я думаю, что почему allow_any_instance_of недоступен в блоке before(:suite), но доступен в блоке before(:each).

Если метод все еще отсутствует, возможно, вы настроили rspec-mocks только для синтаксиса :should. 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.

Ответ 3

Недавно я столкнулся с ситуацией, когда мне нужно было что-то заглушить в блоке before(:all) или before(:context), и нашел, что решения здесь не работают для моего использования.

RSpec docs на before() и after() hooks говорит, что он не поддерживается:

до и после крючков можно определить непосредственно в группах примеров, которые они должен выполняться или в глобальном блоке RSpec.configure.

ПРЕДУПРЕЖДЕНИЕ: установка переменных экземпляра не поддерживается ранее (: suite).

ПРЕДУПРЕЖДЕНИЕ: Mocks поддерживается только до (: пример).

Примечание. Области: example и: context также доступны как: каждый и: все, соответственно. Используйте то, что вы предпочитаете.

Проблема

Я делал драгоценный камень для записи двоичного формата файла, который содержался в timestamp unix-эпохи в двоичном заголовке. Я хотел написать тесты RSpec, чтобы проверить правильность результата выходного файла и сравнить его с бинарным файлом ссылки на тест файл. Чтобы создавать быстрые тесты, мне нужно было записать файл один раз до того, как будут запущены все групповые блоки примера. Чтобы проверить метку времени на ссылочном файле, мне нужно было заставить Time.now() вернуть постоянное значение. Это привело меня к тому, что я попытался заглушить Time.now, чтобы вернуть целевое значение.

Однако, поскольку rspec/mocks не поддерживал stubbing в блоке before(:all) или before(:context), он не работал. Написание файла before(:each) вызвало другие странные проблемы.

К счастью, я наткнулся на номер # 240 rspec-mocks, у которого было решение!

Решение

С 9 января 2014 года (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

Ответ 4

Если вы хотите, чтобы какой-либо конкретный метод вел себя определенным образом для всего тестового набора, нет никаких оснований даже иметь дело с контурами RSpec. Вместо этого вы можете просто (повторно) определить, как метод будет вести себя так, как вы хотите в тестовой среде:

class MyModel
  def my_method
    false
  end
end

Это может быть в spec/spec_helper.rb или аналогичном файле.

Ответ 5

Какую версию RSpec вы используете? Я считаю, что allow_any_instance_of был представлен в RSpec 2.14. Для более ранних версий вы можете использовать:

MyModel.any_instance.stub(:my_method).and_return(false)

Ответ 6

Вы можете использовать следующее, чтобы заглушить метод 'do_this' класса 'Xyz':

allow_any_instance_of(Xyz).to receive(:do_this).and_return(:this_is_your_stubbed_output)

Это заглушает вывод в - ': this_is_your_stubbed_output' из любой точки, где вызывается эта функция.

Вы можете использовать вышеуказанный фрагмент кода в блоке before (: each), чтобы сделать это применимым для всех ваших примеров спецификаций.