2015-02-11 54 views
0

我有一个似乎特定于我的测试套件的问题。RSpec测试只有在按顺序运行时才能正确运行

我有一个持有恒一些默认设置,像这样一个模块:

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.

我没有一个“真实世界”场景,在这种情况下,程序启动一次,这就是但我想要相应地测试它。有任何想法吗?

回答

1

@options是该模块中的类变量。我不确定这在技术上是否正确,但这就是它的表现。作为实验,请在self.options之前打印出@options.object_id。然后运行你的测试。在两种情况下,您都会看到它打印出相同的ID。这就是为什么当你的测试翻转时,你会得到30. @options已经定义,所以@options ||= DEFAULTS没有设置@optionsDEFAULTS

$ 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 
+0

没错,我注意到了同样的事情 - 是否有办法在测试之间“拆除”常量? – Anthony 2015-02-11 20:14:58

+0

当您再次加载文件时,它们将被重置。 '负载(<路径到文件>)' – doesterr 2015-02-11 21:51:46

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