【问题标题】:How do I mock a class using Rspec and Rails?如何使用 Rspec 和 Rails 模拟一个类?
【发布时间】:2017-12-01 21:47:02
【问题描述】:

我正在使用带有 Rspec 3 的 Rails 5。如何在我的 Rspec 方法中模拟一个类?我有以下课程

require 'rails_helper'

describe CryptoCurrencyService do

  describe ".sell" do

    it "basic_sell" do
      last_buy_price = 3000
      last_transaction = MoneyMakerTransaction.new({
        :transaction_type => "buy",
        :amount_in_usd => "100",
        :btc_price_in_usd => "#{last_buy_price}"
      })
      @client = Coinbase::Wallet::Client.new(api_key: ENV['COINBASE_KEY'], api_secret: ENV['COINBASE_SECRET'])
      sell_price = 4000
      assert sell_price > last_buy_price * (1 + MoneyMakerThreshhold.find_buy.pct_change)

      allow(@client).to receive(:sell_price).and_return({"base"=>"BTC", "currency"=>"USD", "amount"=>"#{sell_price}"})

      svc = CryptoCurrencyService.new
      svc.sell(last_transaction)
      last_transaction = MoneyMakerTransaction.find_latest_record
      assert last_transaction.transaction_type, "sell"
    end

  end

end

而不是在行中实际实例化类“Coinbase::Wallet”

@client = Coinbase::Wallet::Client.new(api_key: ENV['COINBASE_KEY'], api_secret: ENV['COINBASE_SECRET'])

我想创建一个模拟,然后我可以插入到我正在测试的服务类中。就目前而言,当我运行东西时,实际的底层类正在被实例化,导致运行时错误......

  1) CryptoCurrencyService.sell basic_sell
     Failure/Error: payment_method = client.payment_methods()[0]

     Coinbase::Wallet::AuthenticationError:
       invalid api key

【问题讨论】:

标签: ruby-on-rails rspec mocking ruby-on-rails-5


【解决方案1】:

rspec 模拟和存根可用于任何类。 例如:

coinbase_mock = double(api_key: ENV['COINBASE_KEY'], api_secret: ENV['COINBASE_SECRET'])
expect(Coinbase::Wallet::Client).to_receive(:new).and_return(coinbase_mock)

然后你可以在coinbase_mock 中添加任何你喜欢的东西,让它像你需要的类一样嘎嘎作响……:)

【讨论】:

    【解决方案2】:

    您可以像这样使用 Ruby 结构:

    Coinbase::Wallet::Client = Struct.new(:api_key, :api_secret)
    @client = Coinbase::Wallet::Client.new(ENV['COINBASE_KEY'], ENV['COINBASE_SECRET'])
    @client.api_key #=> whatever was in ENV['COINBASE_KEY']
    

    然后传入那个对象。

    如果你需要它的行为,你也可以像这样得到:

    Coinbase::Wallet::Client = Struct.new(:api_key, :api_secret) do
      def client_info
        ## logic here
        "info"
      end
    end
    
    @client = Coinbase::Wallet::Client.new(ENV['COINBASE_KEY'], ENV['COINBASE_SECRET'])
    @client.client_info #=> "info"
    

    【讨论】:

      【解决方案3】:

      首选 RSpec(自版本 3 起)样式为

      let(:coinbase_client) { instance_double(Coinbase::Wallet::Client) } 
      # validates that mocked/stubbed methods present in class definitiion
      
      before do
        allow(coinbase_client).to receive(:sell_price).and_return({"base"=>"BTC", "currency"=>"USD", "amount"=>"PRICE YOU PROVIDE"})
      end
      

      docs about instance_double method

      当您将 coinbase_client 作为构造参数注入到内部使用它的类时

      或者如果由于某些原因你不能使用依赖注入,你可以模拟 Coinbase::Wallet::Client 的任何实例

      allow_any_instance_of(Coinbase::Wallet::Client).to receive(... *mock specific method*)
      

      【讨论】:

        猜你喜欢
        • 2023-03-26
        • 2012-01-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多