Как использовать проверку типа сорбета с макетами RSpec? - PullRequest
3 голосов
/ 24 июня 2019

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

2 голосов
/ 24 июня 2019

Mocha mocks (заглушка в тестах) по умолчанию не проходит никаких проверок типов. Это умышленно и считается особенностью; голые макеты делают тесты хрупкими и вызывают проблемы при рефакторинге кода, независимо от проверки типов.

При попытке протестировать метод с использованием макета 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), но принципы должны быть одинаковыми.

1 голос
/ 05 июля 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 по-прежнему выполняет проверки типов, если не используется макет.

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
...