如何在RSpec模拟中使用冰糕类型检查?

时间:2019-06-24 19:09:08

标签: ruby unit-testing rspec rspec-mocks sorbet

我有一个具有冰糕类型签名定义的方法。 尝试在使用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

2 个答案:

答案 0 :(得分:2)

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),但原理应该相同。

答案 1 :(得分:1)

解决方案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仍然可以执行类型检查。

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