I'm using Rails 5 with Rspec 3. How do I mock a class in my Rspec method? I have the following class
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
Instead of actually instantiating the class "Coinbase::Wallet" in the line
@client = Coinbase::Wallet::Client.new(api_key: ENV['COINBASE_KEY'], api_secret: ENV['COINBASE_SECRET'])
I'd like to create mock taht I could then insert into my service class, which I'm testing. As it stands right now, when I run things, the actual underlying class is getting instantiated, resulting the run time error ...
1) CryptoCurrencyService.sell basic_sell Failure/Error: payment_method = client.payment_methods()[0] Coinbase::Wallet::AuthenticationError: invalid api key
3 Answers
Answers 1
rspec mocks and stubs can be used on any class. For example:
coinbase_mock = double(api_key: ENV['COINBASE_KEY'], api_secret: ENV['COINBASE_SECRET']) expect(Coinbase::Wallet::Client).to_receive(:new).and_return(coinbase_mock)
then you can add whatever you like to the coinbase_mock
so that it quacks like the class you need... :)
Answers 2
You could use a Ruby Struct like this:
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']
Then pass that object in.
If you need behavior on it you can also get that like this:
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"
Answers 3
Preferred RSpec (since ver. 3) style would be
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
while you inject coinbase_client as a construction parameter to your classes that use it internally
OR if for some reasons you can't use dependancy injection, you could mock any instance of Coinbase::Wallet::Client with
allow_any_instance_of(Coinbase::Wallet::Client).to receive(... *mock specific method*)
0 comments:
Post a Comment