2015-02-11 55 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