我应该如何使用RSpec全局存根方法?

p.m*_*los 27 rspec ruby-on-rails mocking stubbing ruby-on-rails-3

我正在研究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
Run Code Online (Sandbox Code Playgroud)

但是,启动测试失败,并出现以下错误:

in `method_missing': undefined method `allow_any_instance_of' for #<RSpec::Core::ExampleGroup:0x00000008d6be08> (NoMethodError)

任何线索?我应该如何使用RSpec全局存根方法?

P.

Ioa*_*mas 21

它可能是一个上下文/初始化问题.这样做可以config.before(:each)解决您的问题.


cbl*_*ard 16

不要使用存根方法,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
Run Code Online (Sandbox Code Playgroud)

配置正确后,您应该可以使用allow_any_instance_of.


Tri*_*onX 9

我最近碰到了一个案例,我需要在一个before(:all)或一个before(:context)块中存根,并发现这里的解决方案对我的用例不起作用.

before()和after()钩子上的RSpec文档说它不受支持:

钩子之前和之后可以直接在它们应该运行的示例组中定义,或者在全局RSpec.configure块中定义.

警告:before(:suite)中不支持设置实例变量.

警告:仅在之前支持模拟(例如).

注意::example和:context作用域也可用作:each和:all.使用您喜欢的任何一种.

问题

我正在制作一个用于编写二进制文件格式的宝石,该格式包含在其二进制头文件中的unix epoch时间戳.我想编写RSpec测试来检查输出文件头是否正确,并将其与测试夹具二进制参考文件进行比较.为了创建快速测试,我需要在所有示例组块运行之前将文件写出一次.为了检查参考文件的时间戳,我需要强制Time.now()返回一个常量值.这导致我试图存根Time.now以返回我的目标值.

但是,由于rspec/mocks不支持在一个before(:all)before(:context)块内的存根,它不起作用.写文件before(:each)引起了其他奇怪的问题.

幸运的是,我偶然发现了具有解决方案的rspec-mocks问题#240!

自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
Run Code Online (Sandbox Code Playgroud)


Myr*_*ton 6

如果您希望特定方法在整个测试套件中表现出特定的行为,那么甚至没有理由处理RSpec的存根。相反,您可以简单地(重新)定义方法以在测试环境中表现所需的行为:

class MyModel
  def my_method
    false
  end
end
Run Code Online (Sandbox Code Playgroud)

这可能会进入spec/spec_helper.rb或类似的文件。