I built a client class that sends requests to the Discord API. I mock this client as showed in the sample code below. Please see method #mock_client:
require 'rails_helper'
require 'discord_client'
RSpec.describe some_service_class do
describe '#call' do
let(:client) { mock_client }
it 'does this using discord_client' do
client
described_class.new.call
expect(client).to have_received(:new).once
expect(client).to have_received(:get_guild).once
end
end
private
def mock_client
client = instance_double(DiscordClient)
allow(DiscordClient).to receive(:new).and_return(client)
allow(client).to receive(:get_guild)
allow(client).to receive(:get_user)
client
end
end
However, since I use this client in many services and rake tasks, I don't want to always keep mocking and stubbing it in every spec files I write. Where can I move my method #mock_client so that I can call it in any spec file? Thanks in advance!
In RSpec you can use shared contexts to share your test dependencies (let, let!) or test setup. This is basically a block thats evaluated in the context of the example group its included in:
RSpec.shared_context "Discord mocks" do
let(:client) { instance_double(DiscordClient) }
before do
allow(DiscordClient).to receive(:new).and_return(client)
allow(client).to receive(:get_guild)
allow(client).to receive(:get_user)
end
end
These can either be included manually in indivual specs with include_context
or through your spec setup. Shared contexts are typically placed somewhere in /spec/support
.
On a side note you can decrease the need of stubbing in the first place by providing factory methods which should be used instead of new.get_guild
if you don't need separate initialization and "call" arguments:
class DiscordClient
def self.get_guild(...)
new.get_guild(...)
end
end
Then all you need to do is stub the class methods that your client exposes:
allow(DiscordClient).to receive(:get_guild)
You'll find this pattern used extensively in Service Objects.