samuelgiles / rspec-sorbet

A small gem consisting of helpers for using Sorbet & RSpec together.
MIT License
39 stars 9 forks source link

Exhaustiveness checking fails with doubles #21

Open simoleone opened 1 year ago

simoleone commented 1 year ago

For example code like this:

sig {params(x: T.any(A, B, C)).void}
def foo(x)
  case x
  when A
    do_a(x)
  when B
    do_b(x)
  else
    T.absurd(x)
  end
end

If a test does something like this:

let(:fake_a) { instance_double(A) }
it 'probably does not explode' do
  foo(fake_a)
end

It will hit the T.absurd, resulting in an error like this:

     TypeError:
       Control flow reached T.absurd. Got value: #[InstanceDouble(A) (anonymous)]

I'm not sure there is any way to get around this given the way the case statement works but I figured it couldn't hurt to report in case someone had an idea 😄

multiplegeorges commented 1 year ago

I believe that this issue on rspec-mocks covers the same issue and has a proposed solution.

See a POC here: https://github.com/ImmersiveLabsUkOrg/rspec-mocks/pull/2/files

@samuelgiles Would love your input on how to implement this in rspec-sorbet.

samuelgiles commented 1 year ago

Thanks @multiplegeorges, will take a closer look at this shortly.

The call_validation_error_handler approach that rspec-sorbet uses right now happens wayyy after anything that'd help re: case/#<=>.

I like the idea of an approach that works at a lower level like the one in the POC as it could through Sorbet's public API mean we'd avoid having to maintain this method: https://github.com/samuelgiles/rspec-sorbet/blob/master/lib/rspec/sorbet/doubles.rb#L98-L118 + we might be able to support the doubles being a little more transparent to implementation logic.