Как правильно издеваться над ИТ-сервисами в RSpec? - PullRequest
0 голосов
/ 24 сентября 2019

Я хотел бы узнать, как правильно имитировать вызовы объектов внутри других классов, например, у меня есть следующее действие контроллера:

def show
 service = Action::PartsShow.new(show_params, current_user)
 service.call
 render json: service.part, root: :part, serializer: PartSerializer, include: '**',
        scope: {current_user: current_user}
end

Класс обслуживания выглядит следующим образом.

module Action
  class PartsShow < PartsShowBase
    def find_part
      ...
    end
  end
end

module Action
  class PartsShowBase
    attr_reader :part

    def initialize(params, current_user)
      @params = params
      @current_user = current_user
    end

    def call
      find_part
      reload_part_availability
      reload_part_price if @current_user.present?
    end

    private

    def reload_part_availability
      ReloadPartAvailabilityWorker.perform_async(part.id)
    end

    def reload_part_price
      ExternalData::LauberApi::UpdatePrices.new(@current_user, [part]).execute
    end
  end
end

Я не хочу вызывать действительную Action::PartsShow службу внутри этого действия контроллера и все другие методы, службы + рабочий, потому что это делает тест очень медленным.Я хочу проверить, вызывается ли этот сервис, и высмеивать остальные сервисы.Я не хочу вызывать их в своих тестах, я хочу издеваться над ними.

Мой тест выглядит так:

RSpec.describe PartController, type: :request do
  describe 'GET #show' do
    let(:part) { create(:part) }

    subject { get "/api/v1/parts/#{part.id}" }

    expect(response_body).to eq(200)
    # ...
  end
end

Не могли бы вы показать мне, как правильно издеваться над ним?Я читал о насмешках и заглушках RSpec, но я в замешательстве.Буду признателен за вашу помощь.

Ответы [ 2 ]

1 голос
/ 24 сентября 2019

С помощью rspec-mocks gem вы можете использовать allow_any_instance_of.Обычно эта часть находится в блоке before.

Фактически, Action::PartsShow отвечает за загрузку части, поэтому нет необходимости пропускать два метода экземпляра: call и part.Вы можете упростить его, вернув деталь из call.

module Action
  class PartsShowBase
    #attr_reader :part

    def call
      find_part # assign @part
      reload_part_availability
      reload_part_price if @current_user.present?
      @part
    end
    ...
end
RSpec.describe PartController, type: :request do
  before :all do
    allow_any_instance_of(Action::PartsShow).to receive(:call).and_return(returned_part)
  end

Ссылка

https://relishapp.com/rspec/rspec-mocks/v/3-5/docs/working-with-legacy-code/any-instance

0 голосов
/ 27 сентября 2019

Предполагая, что find_part вызывает Part.find(id), вы можете добавить:

before do
  allow(Part).to receive(:find).with(part.id) { part }
end

, чтобы поиск записей всегда возвращал ваш тестовый объект.Я также предлагаю немного переработать вашу спецификацию:

RSpec.describe PartController, type: :request do
  describe 'GET #show' do
    let(:request) { get "/api/v1/parts/#{part.id}" }
    let(:part)    { create(:part) }

    it '200s' do
      request
      expect(response).to have_http_status(:success)
    end
  end
end

Если в вашем проекте помощников пути , я бы также рекомендовал использовать их вместо строки пути.

...