Как использовать проверку типа сорбета с макетами RSpec?

У меня есть метод, который имеет определение подписи типа сорбета. При попытке смоделировать этот метод в тестах с использованием 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

person Rahil Shah    schedule 24.06.2019    source источник


Ответы (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 (в Stripe, где разрабатывается Sorbet, мы используем Mocha), но принципы должны быть одинаковыми.

person jez    schedule 24.06.2019
comment
Понятно! Спасибо. Я отрефакторил тестовый код, как вы предложили, и теперь он работает нормально. - person Rahil Shah; 25.06.2019
comment
Я не уверен, что это оправдание полностью подходит для макетов RSpec, поскольку (я полагаю) у них есть несколько отличных от мокко свойств. Создали задачу для обсуждения: github.com/rspec/rspec-mocks/issues/1286 - person Xavier Shay; 30.07.2019

Решение 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

person Jan Jedrychowski    schedule 05.07.2019
comment
Вышеописанное сделано в этом геме: github.com/tricycle/rspec-sorbet - person Allenaz; 09.11.2020