【问题标题】:Stub unimplemented method in rspecrspec中的存根未实现方法
【发布时间】:2015-09-02 09:30:21
【问题描述】:

我正在测试我的模块,我决定将它与匿名类进行测试:

  subject(:klass) { Class.new { include MyModule } }

MyModuleklass 中使用方法name。为了让我的规范工作,我需要存根这个方法name(这是未实现的)。于是我写了:

subject { klass.new }
allow(subject).to receive(:name).and_return('SOreadytohelp') }

但它引发了:

 RSpec::Mocks::MockExpectationError: #<#<Class:0x007feb67a17750>:0x007feb67c7adf8> does not implement: name
from spec-support-3.3.0/lib/rspec/support.rb:86:in `block in <module:Support>'

如何在不定义的情况下存根这个方法?

【问题讨论】:

  • 我不知道这是否是答案,但您有错字; subjet { klass.new }。不应该是:subject { klass.new }(缺少“c”)。试试看,让我们知道!

标签: ruby-on-rails ruby ruby-on-rails-4 rspec


【解决方案1】:

RSpec 引发此异常,因为存根原始对象上不存在的方法没有用。

模拟方法总是容易出错,因为模拟的行为可能与原始实现不同,因此即使原始实现返回错误(或什至不存在),规范也可能成功。允许模拟不存在的方法是完全错误的。

因此,我认为您不应尝试绕过此异常。只需在您的类中添加一个 name 方法,如果在测试环境之外运行,该方法会引发明显的异常:

def self.name
  raise NoMethodError  # TODO: check specs...
end

【讨论】:

  • name 应该是返回'SOreadytohelp'的实例方法
  • @Stefan:我同意,这也是个好主意。取决于重点:强调规范和代码是同步的,或者有什么需要修复的......
  • NotImplementedError 的定义:NotImplementedError Raised when a feature is not implemented on the current platform. For example, methods depending on the +fsync+ or +fork+ system calls may raise this exception if the underlying operating system or Ruby runtime does not support them. 应该是NoMethodErrorRaised when a method is called on a receiver which doesn’t have it defined and also fails to respond with +method_missing+ 需要更正
【解决方案2】:
subject(:klass) do 
  Struct.new(:name) do
   include MyModule
  end
end

http://ruby-doc.org/core-2.2.0/Struct.html

【讨论】:

    【解决方案3】:

    我认为,如果您正在编写的测试专注于您的 MyModule 模块,并且该模块依赖于它所混入的类中的实例方法,那么我认为该方法应该在测试模块时使用的匿名类。例如:

    module MyModule
      def call_name
        # expected implementation of #name to be
        # in the class this module is mixed into
        name
      end
    end
    
    RSpec.describe MyModule do
      let(:my_module_able) do
        Class.new do
          include MyModule
    
          # We don't care what the return value of this method is;
          # we just need this anonymous class to respond to #name
          def name
            'Some Name that is not SOReadytohelp'
          end
        end.new
      end
    
      describe '#call_name' do
        let(:name) { 'SOReadytohelp' }
    
        before do
          allow(my_module_able).to receive(:name).and_return(name)
        end
    
        it 'returns the name' do
          expect(my_module_able.call_name).to eq(name)
        end
      end
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-03
      • 1970-01-01
      • 2012-10-24
      • 2014-09-23
      • 2013-08-22
      相关资源
      最近更新 更多