如何使用Rspec和Rails模拟一个类?

时间:2017-12-01 21:47:03

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

我使用Rails 5和Rspec 3.如何在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'])

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

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

     Coinbase::Wallet::AuthenticationError:
       invalid api key

3 个答案:

答案 0 :(得分:7)

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,这样它就像你需要的类一样嘎嘎...:)

答案 1 :(得分:2)

您可以像这样使用Ruby Struct:

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"

答案 2 :(得分:1)

首选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*)