动态类

验证实例替身不支持类报告不存在的方法,因为需要实际的类实例才能进行验证。当使用 method_missing 时,这种情况很常见。

有几种解决方法。如果对象已经加载,您可以考虑使用 object_double,但这在隔离测试时无法使用。或者,您可以直接实现方法(调用 super 返回 method_missing 定义)。

其中一些类可能具有在运行时在对象上定义这些方法的方法。(例如,ActiveRecord 这样做是为了从数据库列定义方法。)对于这些情况,我们提供了一个 API,可以在创建时用于自定义验证替身。我们在 rspec-rails 中自己使用它来为您设置一些便利功能。

这些类型的 method 在类级别(使用 class_double)得到支持,但是,由于 respond_to? 可以直接在类上查询。

背景

给定一个名为“lib/fake_active_record.rb”的文件,其中包含

class FakeActiveRecord
  COLUMNS = %w[name email]

  def respond_to_missing?(method_name)
    COLUMNS.include?(method_name.to_s) || super
  end

  def method_missing(method_name, *args)
    if respond_to?(method_name)
      instance_variable_get("@#{method_name}")
    else
      super
    end
  end

  def self.define_attribute_methods
    COLUMNS.each do |name|
      define_method(name) { instance_variable_get("@#{name}") }
    end
  end
end

给定一个名为“spec/user_spec.rb”的文件,其中包含

require 'user'

RSpec.describe User do
  it 'can be doubled' do
    instance_double("User", :name => "Don")
  end
end

使用 method missing 失败

给定一个名为“lib/user.rb”的文件,其中包含

require 'fake_active_record'

class User < FakeActiveRecord
end

我运行 rspec spec/user_spec.rb

那么输出应该包含“1 个示例,1 个失败”。

使用显式定义的解决方法

给定一个名为“lib/user.rb”的文件,其中包含

require 'fake_active_record'

class User < FakeActiveRecord
  def name;  super end
  def email; super end
end

我运行 rspec spec/user_spec.rb

那么所有示例都应该通过。

使用回调的解决方法

给定一个名为“lib/user.rb”的文件,其中包含

require 'fake_active_record'

class User < FakeActiveRecord
end

以及一个名为“spec/fake_record_helper.rb”的文件,其中包含

RSpec.configuration.mock_with(:rspec) do |config|
  config.before_verifying_doubles do |reference|
    reference.target.define_attribute_methods
  end
end
#
# or you can use:
#
# RSpec::Mocks.configuration.before_verifying_doubles do |reference|
#   reference.target.define_attribute_methods
# end

我运行 rspec -r fake_record_helper spec/user_spec.rb

那么所有示例都应该通过。