10

我有一个具有冰糕类型签名定义的方法。尝试在使用 RSpec 的测试中模拟此方法时,出现类型不匹配错误。我正在尝试了解如何解决此问题,并且可以添加基于 RSpec 的测试而不影响冰糕类型检查。

sig {params(login_context: LoginContext, company_id: String).returns(T::Boolean)}
  def populate_dummy_data(login_context, company_id)

测试代码:

@login_context = double(LoginContext, :requester => @requester) # Creates an instance of type Rspec::Mocks::double

错误:

expected no Exception, got #<TypeError: Parameter ‘login_context’: Expected type LoginContext, got type RSpec::Mocks::Double wit...a_populator_spec.rb:42
4

2 回答 2

8

解决方案1:

与适当的类一起使用instance_double并模拟它的is_a?. 为此,请在全局范围内执行猴子修补:

require 'rspec/mocks'

class RSpec::Mocks::InstanceVerifyingDouble
  def is_a?(expected)
    @doubled_module.target <= expected || super
  end
end

解决方案2:

选择性地,当由模拟引起时不要引发异常。这种方式 Sorbet 仍然执行类型检查,除非使用模拟。

require 'sorbet-runtime'

RSpec.configure do |config|
  config.before :each, sorbet: :mocks do
    T::Configuration.inline_type_error_handler = proc do |error|
      raise error unless error.message.include? "got type RSpec::Mocks"
    end

    T::Configuration.call_validation_error_handler = proc do |_signature, opts|
      raise TypeError.new(opts[:pretty_message]) unless opts[:message].include? "got type RSpec::Mocks"
    end
  end


  config.after :each, sorbet: :mocks do
    T::Configuration.inline_type_error_handler = nil
    T::Configuration.call_validation_error_handler = nil
  end
end

于 2019-07-05T14:46:59.157 回答
6

Mocha 模拟(测试中的存根)默认不会通过任何类型检查。这是经过深思熟虑的,被认为是一个特征;裸模拟使测试变得脆弱,并且在重构代码时往往会导致问题,无论类型检查如何。

当尝试使用未通过类型检查的 Mocha 模拟测试方法时,我们建议重写测试以不使用 Mocha 模拟。任何一个:

  • 创建对象的真正实例,并.stubs仅用于替换某些方法。
  • 编写辅助函数以使用假数据创建对象的真实实例。

在最坏的情况下,您可以通过存根is_a?使 Mocha 模拟通过类型检查,但请避免这样做。它会导致脆弱的测试并使代码更难推理。如果你必须:

# NOT RECOMMENDED!

fake_llama = stub
fake_llama.stubs(:llama_count).returns(17)
fake_llama.stubs(:is_a?).with(M::Llama).returns(true)

我不熟悉 RSpec 的模拟和 Mocha 的模拟之间的区别(在开发 Sorbet 的 Stripe,我们使用 Mocha),但原则应该是相同的。

于 2019-06-24T20:56:15.403 回答