Ruby on rails 如何使用RSpec中的allow方法模拟模块内控制器内的函数(模块>;控制器>;函数)

Ruby on rails 如何使用RSpec中的allow方法模拟模块内控制器内的函数(模块>;控制器>;函数),ruby-on-rails,ruby,rspec,rspec-rails,Ruby On Rails,Ruby,Rspec,Rspec Rails,我正在尝试在RSpec中编写allow方法。我的rails控制器是 module Users class ProfilesController < ApplicationController # Update user profile def update payload = { name: params[:user][:name],email: params[:user][:email]} response = send_request_t

我正在尝试在RSpec中编写
allow
方法。我的rails控制器是

module Users
  class ProfilesController < ApplicationController

    # Update user profile
    def update

      payload = { name: params[:user][:name],email: params[:user][:email]}
      response = send_request_to_update_in_company(payload)
      if response['code'] == 200
        if  User.first.update(user_params) 
          render json: { message: "User successfully updated"}, status: :ok
        else
          head :unprocessable_entity
        end
      else
       render json: { error: 'Error updating user in Company' },status: :unprocessable_entity
     end

   end

   private

   def send_request_to_update_in_comapny(payload)
    response = Api::V1::CompanyRequestService.new(
      payload: payload.merge(company_api_access_details),
      url: 'customers/update_name_email',
      request_method: Net::HTTP::Post
      ).call
    JSON.parse(response.body)
   end
  end
end
我在终端中遇到以下错误

Users::ProfilesController does not implement: send_request_to_update_in_comapny
enter code here

有了allow_任意一个实例,我就能让代码正常工作。但是我如何使用allow实现它呢?

是的,的
allow\u any\u instance\u有效,因为顾名思义,它允许
用户::ProfilesController
的任何instance用模拟返回值响应实例方法
向公司的
然而,你的线路

allow(Users::ProfilesController).to receive(:send_request_to_update_in_company)
正在告诉RSpec模拟名为
类方法将\u请求发送到\u company
中的\u update\u,该方法不存在。因此,您会看到这样的错误消息

你不会说你的测试在哪里,但是一般来说,不管它在哪里,测试或者删除一个私有方法都不是一个好主意

我倾向于创建一个mock
Api::V1::CompanyRequestService
对象来返回一个伪响应,然后控制器代码可以按预期解析该响应并生成预期的JSON。比如说

mock_request = instance_double(Api::V1::CompanyRequestService)
allow(mock_request).to receive(:call).and_return('{"code": 500}')
allow(Api::V1::CompanyRequestService).to receive(:new).and_return(mock_request)
另一种方法可能是不使用您的服务,而是使用诸如
VCR
WebMock
之类的工具在网络层提供模拟的JSON值-您的代码可以认为它在向internet发出呼叫,但实际上它会返回您在测试中定义的响应。

这样如何:

规格/请求/用户/配置文件\u控制器\u规格.rb

require 'rails_helper'

RSpec.describe "Users::ProfilesControllers", type: :request do
  describe "Test call to special function: " do
    let(:controller) { Users::ProfilesController.new }
    it "Should response to code 500" do
      response = controller.send_request_to_update_in_company("test")
      expect(response).to eq({"code"=>"500", "test1"=>"abc", "test2"=>"def"})
    end
    it "Should return to true" do
      response = controller.true_flag?
      expect(response).to eq(true)
    end
  end
end
app/controllers/users/profiles\u controller.rb

module Users
  class ProfilesController < ApplicationController
    # Update user profile
    def update
      payload = { name: params[:user][:name],email: params[:user][:email]}
      response = send_request_to_update_in_company(payload)
      Rails.logger.debug "Ok71 = response['code'] = #{response['code']}"
      # if response['code'] == 200
      #   if  User.first.update(user_params) 
      #     render json: { message: "User successfully updated"}, status: :ok
      #   else
      #     head :unprocessable_entity
      #   end
      # else
      #  render json: { error: 'Error updating user in Company' },status: :unprocessable_entity
      # end
    end

    # Not private, and not mistake to 'send_request_to_update_in_comapny'
    def send_request_to_update_in_company(payload)
      response = Api::V1::CompanyRequestService.new(
        payload: "for_simple_payload_merge_values",
        url: 'for_simple_customers/update_name_email',
        request_method: "for_simple_request_method"
        ).call
      Rails.logger.debug "Ok66 = Start to log response"
      Rails.logger.debug response
      JSON.parse(response.body)
    end

    # Simple function to test
    def true_flag?
      true
    end

  end
end
模块用户
类ProfilesController
app/services/api/v1/company\u request\u service.rb

class Api::V1::CompanyRequestService < ActionController::API
  def initialize(payload="test1", url="test2", request_method="test3")
    @payload = payload
    @url = url
    @request_method = request_method
  end
  def call
    @object = Example.new
    @object.body = {code: "500", test1: "abc", test2: "def"}.to_json
    return @object
  end
end

class Example
  attr_accessor :body
  def initialize(body={code: "000", test1: "init_value_abc", test2: "init_value_def"}.to_json)
    @body = body
  end
end
class Api::V1::CompanyRequestService
我使用简单的代码来模拟您的项目。修改它以适合您的工作环境!告诉我你的想法。谢谢大家!

class Api::V1::CompanyRequestService < ActionController::API
  def initialize(payload="test1", url="test2", request_method="test3")
    @payload = payload
    @url = url
    @request_method = request_method
  end
  def call
    @object = Example.new
    @object.body = {code: "500", test1: "abc", test2: "def"}.to_json
    return @object
  end
end

class Example
  attr_accessor :body
  def initialize(body={code: "000", test1: "init_value_abc", test2: "init_value_def"}.to_json)
    @body = body
  end
end