【问题标题】:RSpec testing of a class which uses a gem object as an instance variable使用 gem 对象作为实例变量的类的 RSpec 测试
【发布时间】:2016-09-21 01:48:08
【问题描述】:

所以我对 Rspec 还是很陌生,我试图弄清楚如何为一个将对象作为构造函数参数并将该对象设置为实例变量的类编写测试。然后它在其他方法中调用该实例变量的对象方法。

例子:

class ClassA
  def initialize(string_object, gem_object)
    @instance_variable1 = gem_object
    @string = string_object
  end

  def check_validity?(some_arg)
    unless @instance_variable1.gemObjectMethod1.gemObjectMethod2(some_arg).empty?
      return true
    end
    false
  end
  ..
  ..
end

我对如何为此编写规范感到非常迷茫。一方面,我真的不明白指定构造函数实际上需要什么。我意识到我必须找到某种方法来模拟或存根我作为参数得到的 gem_object ,但我不确定如何。

对于下一个方法,我目前尝试的是:

describe '#check_validity?' do
  context 'gets empty list' do
    let (:actual) { subject.check_validity?("sample") }
    before do
      allow(subject).to receive(@instance_variable1.gemObjectMethod1.gemObjectMethod2).with("sample").and_return([])
    end
    it 'returns false' do
      expect(actual).to be false
    end
  end
end

但这给了我与我的构造函数有关的错误,说它需要 2 个参数,但给出了 0。

任何帮助将不胜感激!此外,我在网上找不到任何关于指定构造函数及其参数被模拟的内容。也许我找错地方了,或者可能遗漏了一些明显的东西,因为这是我第一次使用 BDD。

【问题讨论】:

    标签: ruby rspec


    【解决方案1】:

    在 RSpec 中,'receive' 是一个接受表示方法名称的符号的方法。 (它允许您链接一个接受预期参数列表的“with”方法。)要修复前块,您可以这样做:

    before do
      allow(subject.instance_variable_get(:@instance_variable1).gemObjectMethod1).to receive(:gemObjectMethod2).with("sample").and_return([])
    end
    

    不过,这纯粹是丑陋的一面,表明有些地方出了问题。它是。该代码严重违反了law of demeter,并且正在对其进行测试。

    作为清理它的第一次尝试,您可能会考虑一种“缓存”调用@instance_variable1.gemObjectMethod1 的结果的方法。假设第一个方法返回一组可枚举的小部件。您可以更改您的课程以包含以下内容:

    def check_validity(a_string)
      widgets.gemObjectMethod2(a_string).empty?
    end
    
    private
    
    def widgets
      @widgets ||= @instance_variable1.gemObjectMethod1
    end
    

    您的类仍然对 gem 对象了解得太多,但现在您已经将其分解为可以重构查找小部件的方式——可能是不同的 gem 或您自己的实现。出于测试目的,您可以通过模拟小部件将该决定与测试隔离开来。

    let(:gem_widgets) do
      instance_double(GemObjectMethod1ResultClass, gemObjectMethod2: true)
    end
    
    before do
      allow(subject).to receive(:widgets).and_return(gem_widgets)
      allow(gem_widgets).to receive(:gemObjectMethod2).with("sample").
        and_return([])
    end
    
    it 'should pass with "sample"' do
      expect(actual).to eql true
    end
    

    【讨论】:

    • 谢谢,这很有帮助。你有关于如何测试构造函数的建议吗?我正在考虑在调用 new 之前测试一个对象不存在,然后在调用 new 之后测试它具有正确的实例变量。我对如何测试对象是否已创建无动于衷。
    • 我经常对这样的类做的是通过属性读取器(例如,在 ClassA 顶部的 attr_reader :string_object、:gem_object )提供实例变量。如果你这样做了,那么测试分配就非常简单了——在实例化之后检查 getter 方法的值。
    • 谢谢。不过我确实有一个问题。通过创建widgets方法,我是不是需要确切地找出gemObjectMethod1返回的内容,这不违反抽象原则吗?
    • 如果您在上面复制/跳过我的想法,那么您将检查 gemObjectMethod1 返回的内容,这是真的。我不确定您是否会比粘贴的代码已经违反抽象规则——除非 gemObjectMethod2 恰好是 Enumerable 或类似方法上可用的常用方法。使用验证双精度(上面的实例双精度)的优点是它根据您打算调用的方法验证类的接口;这对宝石来说可能很重要。另一种选择是通用的 double('widgets', gemObjectMethod2: true)
    • 有道理。谢谢
    猜你喜欢
    • 1970-01-01
    • 2022-12-22
    • 2017-04-16
    • 1970-01-01
    • 1970-01-01
    • 2012-10-15
    • 1970-01-01
    • 1970-01-01
    • 2012-12-13
    相关资源
    最近更新 更多