RSpec测试仅在按顺序运行时才能正确运行

时间:2015-02-11 19:45:22

标签: ruby rspec rspec3

我有一个似乎特定于我的测试套件的问题。

我有一个模块,它在一个常量中包含一些默认值,如下所示:

module MyModule

  DEFAULTS = {
    pool: 15
  }
  def self.options
    @options ||= DEFAULTS
  end

  def self.options=(opts)
    @options = opts
  end

end

module MyModule
  class MyClass

    def options
      MyModule.options
    end

    def import_options(opts)
      MyModule.options = opts
    end

  end
end

我允许程序在没有选项的情况下启动,或者用户可以指定选项。如果没有给出选项,我们使用默认值,但如果给出选项,我们使用它。示例测试套件如下所示:

RSpec.describe MyModule::MyClass do
  context "with deafults" do
    let(:my) { MyModule::MyClass.new }
    it 'has a pool of 15' do
      expect(my.options[:pool]).to eq 15
    end
  end
  context "imported options" do
    let(:my) { MyModule::MyClass.new }
    it 'has optional pool size' do
      my.import_options(pool: 30)
      expect(my.options[:pool]).to eq 30
    end
  end
end

如果那些测试按顺序运行,很好,一切都过去了。如果它以反向运行(第二次测试首先进行),则第一次测试的池大小为30.

我没有“真实世界”的情况会发生这种情况,程序启动一次,就是这样,但我想相应地测试一下。有什么想法吗?

2 个答案:

答案 0 :(得分:1)

@options是该模块中的类变量。我不确定它在技术上是否正确,但这就是它的表现方式。作为实验,请在@options.object_id中访问self.options之前打印@options。然后运行测试。您会看到在两种情况下都打印出相同的ID。这就是为什么当您的测试被翻转时,您得到30. @options ||= DEFAULTS已经定义,因此@options未将DEFAULTS设置为$ cat foo.rb module MyModule DEFAULTS = { pool: 15 } def self.options puts "options_id: #{@options.object_id}" @options ||= DEFAULTS end def self.options=(opts) @options = opts end end module MyModule class MyClass def options MyModule.options end def import_options(opts) MyModule.options = opts end end end puts "pool 30" my = MyModule::MyClass.new my.import_options(pool: 30) my.options[:pool] puts puts "defaults" my = MyModule::MyClass.new my.options[:pool]

$ ruby foo.rb
pool 30
options_id: 70260665635400

defaults
options_id: 70260665635400

运行它......

{{1}}

答案 1 :(得分:0)

您始终可以使用before(:each)

before(:each) do
  MyModule.options = MyModule::DEFAULTS
end

旁注 - 可能是配置类。

类似的东西:

module MyModule
  class Configuration
    def initialize
      @foo = 'default'
      @bar = 'default'
      @baz = 'default'
    end

    def load_from_yaml(path)
      # :)
    end

    attr_accessor :foo, :bar, :baz
  end
end

然后你可以添加这样的东西:

module MyModule
  class << self
    attr_accessor :configuration
  end

  # MyModule.configure do |config|
  #   config.baz = 123
  # end
  def self.configure
    self.configuration ||= Configuration.new
    yield(configuration)
  end
end

最后,您将以更有意义的方式重置配置

before(:each) do
  MyModule.configuration = MyModule::Configuration.new
end