rubyunit-testingrspecrspec-mockssorbet

How to use sorbet type checking with RSpec mocks?


I have a method that that has a sorbet type signature definition. While trying to mock this method in tests using RSpec I get a type mismatch error. I'm trying to understand how I can resolve this issue and can add RSpec based tests without affecting sorbet type check.

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

Test Code:

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

Error:

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

Solution

  • Mocha mocks (stub in tests) will not pass any type checks by default. This is deliberate and considered a feature; bare mocks make tests brittle and tend to cause problems when refactoring code, regardless of type checking.

    When trying to test a method using a Mocha mock that fails a type check, we recommend rewriting the test to not use Mocha mocks. Either:

    In the worst case, you can stub is_a? to make a Mocha mock pass a type check, but please avoid doing this. It results in brittle tests and makes code harder to reason about. If you must:

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

    I'm not familiar with the differences between RSpec's mocks and Mocha's mocks (at Stripe where Sorbet is developed we use Mocha) but the principles should be the same.