10

我正在使用 RSpec 在 gem 中测试类级别的实例变量(和设置器)。我需要测试以下内容:

  1. 如果从未使用过设置器,则会提供正确的默认值。
  2. 变量可以通过设置器成功更新。

显然这里有一个运行顺序问题。如果我使用设置器更改值,我会忘记默认值是什么。我可以在 setter 测试之前将它保存到一个变量中,然后在最后重置该值,但这只有在所有 setter 测试都遵循相同的做法时才能保护我。

测试变量默认值的最佳方法是什么?

这是一个简单的例子:

class Foo
  class << self
    attr_accessor :items
  end
  @items = %w(foo bar baz) # Set the default
  ...
end

describe Foo do

  it "should have a default" do
    Foo.items.should eq(%w(foo bar baz))
  end

  it "should allow items to be added" do
    Foo.items << "kittens"
    Foo.items.include?("kittens").should eq(true)
  end
end
4

3 回答 3

13
class Foo
  DEFAULT_ITEMS = %w(foo bar baz)

  class << self
    attr_accessor :items
  end

  @items = DEFAULT_ITEMS
end

describe Foo do
  before(:each) { Foo.class_variable_set :@items, Foo::DEFAULT_ITEMS }

  it "should have a default" do
    Foo.items.should eq(Foo::DEFAULT_ITEMS)
  end

  it "should allow items to be added" do
    Foo.items << "kittens"
    Foo.items.include?("kittens").should eq(true)
  end
end

或者也许更好的方法是重新加载类

describe 'items' do
  before(:each) do
    Object.send(:remove_const, 'Foo')
    load 'foo.rb'
  end
end
于 2012-12-28T01:26:04.797 回答
10

如果你的班级有你想测试的内部状态,我发现使用class_variable_get一种很好的方法来解决这个问题。这不需要您公开类中的任何变量,因此类可以保持不变。

it 'increases number by one' do
    expect(YourClass.class_variable_get(:@@number)).to equal(0)
    YourClass.increase_by_one()
    expect(YourClass.class_variable_get(:@@number)).to equal(1)
end

我知道这不是您在问题中所要求的,而是在标题中,这让我来到了这里。

于 2013-12-11T12:13:28.247 回答
2

我发现这个问题追求一个稍微不同的问题——清除 rspec 示例之间的缓存类变量。

在一个模块中,我有一个昂贵的类配置,我像这样缓存它:

module Thingamizer
  def config
    @config ||= compute_config_the_hard_way()
  end
end

class Thing
   extend Thingamizer
end

在我对 Thing 的 rspec 测试中,compute_config_the_hard_way仅第一次调用。后续调用使用缓存版本,即使我compute_config_the_hard_way在其他测试中模拟返回不同的东西。

我通过在每个示例之前清除 @config 解决了这个问题:

 before { Thing.instance_variable_set(:@config, nil) }

现在我被挂断的是@config 是一个变量,而不是一个实例变量。我尝试了很多class_variable_set没有运气的变体。

这里的问题是 Thing(类)实际上是 Class 的一个实例。所以在类方法中看似是类变量的东西实际上是一个实例变量,在Class的一个实例中(即Thing)。一旦我把我的头脑围绕在这个想法上,使用instance_variable_set而不是class_variable_set完全有意义。

有关将类变量作为实例变量的讨论,请参阅在类方法中使用实例变量 - Ruby

于 2019-05-22T16:08:55.697 回答