【问题标题】:RSpec Class Variable TestingRSpec 类变量测试
【发布时间】:2012-12-13 08:35:22
【问题描述】:

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

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

显然这里存在运行顺序问题。如果我使用设置器更改值,我会忘记默认值是什么。我可以在 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

【问题讨论】:

  • 哦,所以你的意思是Foo.bar = 1
  • 是的,代码更新了一个例子。

标签: ruby rspec


【解决方案1】:
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

【讨论】:

  • 这是一个类级别的@variable。如果我使用before(:each),则可以在代码中更改默认值并且测试不会注意到。
  • 很好的解决方案。我的愿景是让代码测试工作,而不是让我的代码更可测试。感谢您重回正轨! :)
【解决方案2】:

如果你的班级有你想测试的内部状态,我发现使用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

我知道这不是你在问题中要求的,但它在标题中,这让我来到这里。

【讨论】:

  • 感谢您的加入。你是对的,这有点不同,但希望这能帮助其他找到这篇文章并正在寻找与我解决的问题有点不同的人。
【解决方案3】:

我发现这个问题追求一个稍微不同的问题——清除 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 变量,而不是实例变量。我尝试了class_variable_set 的许多变体,但没有运气。

这里的问题是 Thing(类)实际上是 Class 的 instance。因此,在类方法中似乎是类变量实际上是一个实例变量,在 Class 的实例(即 Thing)中。一旦我想到这个想法,使用instance_variable_set 而不是class_variable_set 就非常有意义。

请参阅Using Instance Variables in Class Methods - Ruby,了解将类变量作为实例变量的讨论。

【讨论】:

  • 这太完美了!我一直在寻找几乎完全相同的场景来做到这一点!谢谢!
猜你喜欢
  • 2017-04-20
  • 2022-12-22
  • 1970-01-01
  • 2012-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多