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
Mocking is a technique in test-driven development (TDD) that involves using fake dependent objects or methods in order to write a test. There are a couple of reasons why you may decide to use mock objects: As a replacement for objects that don't exist yet.
Stub: a dummy piece of code that lets the test run, but you don't care what happens to it. Substitutes for real working code. Mock: a dummy piece of code that you verify is called correctly as part of the test. Substitutes for real working code.
The it Keyword The word it is another RSpec keyword which is used to define an “Example”. An example is basically a test or a test case. Again, like describe and context, it accepts both class name and string arguments and should be used with a block argument, designated with do/end.
A Test Stub is a fake thing you stick in there to trick your program into working properly under test. A Mock Object is a fake thing you stick in there to spy on your program in the cases where you're not able to test something directly.
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... :)
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"
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*)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With