RSpec Mock Object Example
Here's an example of a simple mock I did for a controller test in a rails application:
before(:each) do
@page = mock_model(Page)
@page.stub!(:path)
@page.stub!(:find_by_id)
@page_type = mock_model(PageType)
@page_type.stub!(:name)
@page.stub!(:page_type).and_return(@page_type)
end
In this case, I'm mocking the Page & PageType models (Objects) as well as stubbing out a few of the methods I call.
This gives me the ability to run a tests like this:
it "should be successful" do
Page.should_receive(:find_by_id).and_return(@page)
get 'show', :id => 1
response.should be_success
end
I know this answer is more rails specific, but I hope it helps you out a little.
Edit
Ok, so here is a hello world example...
Given the following script (hello.rb):
class Hello
def say
"hello world"
end
end
We can create the following spec (hello_spec.rb):
require 'rubygems'
require 'spec'
require File.dirname(__FILE__) + '/hello.rb'
describe Hello do
context "saying hello" do
before(:each) do
@hello = mock(Hello)
@hello.stub!(:say).and_return("hello world")
end
it "#say should return hello world" do
@hello.should_receive(:say).and_return("hello world")
answer = @hello.say
answer.should match("hello world")
end
end
end
Mocking object that are generated dynamically + rspec-mock
Instances of the Java::RandomRequest
and Java::RandomResponse
that you create in spec do not match those that you create in create_address
.
Try stubbing the classes to return the values you need like:
let(:request) { instance_double(Java::RandomRequest) }
let(:response) { instance_double(Java::RandomResponse) }
before do
allow(Java::RandomRequest).to receive(:new).and_return(request)
allow(Java::RandomResponse).to receive(:new).and_return(response)
end
Also, you can collapse allow
in before
with expect... have_received
after create_address
call to a spy called before the create_address
action, e.g.:
# no before block
it 'should create the address' do
expect(AddressCreator.singleton)
.to receive(:create_address)
.with(some_key, request, response) do |request|
expect(request.is_success).to eq(response.is_success)
end
result = AddressCreator.instance.client.create_address some_key
expect(result).to eq(response)
end
Another option would be to use kind_of
/instance_of
/duck_type
argument matchers instead of stubs if you don't intend to check how exactly request and response are initialized, e.g. something like:
# no before
it 'should create the address' do
expect(AddressCreator.singleton).to receive(:create_address)
.with(kind_of(String), instance_of(Java::RandomRequest), duck_type(:is_success)) do |request|
expect(request.is_success).to be_true
end
AddressCreator.instance.client.create_address some_key
end
Lastly, you don't have to initialize some_key
with real secure key, double would be enough if you don't process and just pass to some stubbed class in the end:
let(:some_key) { double }
How to properly mock objects in RSpec?
bucket and key parameters differ in actual calling and mocking.
Use below code it works:
describe S3DownloadUrlGenerator do
before do
allow(Aws::S3::Client).to receive(:new) { s3_client }
end
let(:s3_client) { spy("s3 client") }
let(:presigner) { spy("s3 presigner") }
it "generates download URL for a file" do
expect(Aws::S3::Presigner).to receive(:new).with(client: s3_client).and_return(presigner)
expect(presigner).to receive(:presigned_url).with(
:get_object,
bucket: "my-bucket",
key: "Test_file.txt",
response_content_disposition: "attachment",
).and_return("https://www.example.com")
expect(described_class.new("Test_file.txt").presigned_url).to eq("https://www.example.com")
end
end
With rspec, can I mock an object that is inside the method I am testing?
You can indeed mock out other methods in a RSpec test. If the two methods you mentioned are inside a class, Foo
, you would do something like this to make sure that some_other_method
is called:
subject{ Foo.new }
it "should do whatever you're testing" do
subject.should_receive(:some_other_method).and_return(5)
subject.some_method
end
If you don't need to assert that is was called, just assert the results of some_method
, you can do something like this instead:
subject{ Foo.new }
it "should do whatever you're testing" do
subject.stub(:some_other_method).and_return(5)
subject.some_method.should eq(6)
end
The above examples assume you're using RSpec 2. If you're using RSpec 1, you'll need to use stubs
instead of stub
.
If your methods are defined outside of a class, they're really defined on the class Object
, so just use Object
instead of Foo
in the examples above.
For more information about mocks in RSpec, check out http://relishapp.com/rspec/rspec-mocks for RSpec 2 or http://rspec.info/documentation/mocks/ for RSpec 1.
How to properly mock itnernal services in RSpec?
With rspec-mocks gem, you can use allow_any_instance_of
. Usually, this part lies in before
block.
In fact,Action::PartsShow
is responsible for loading a part, so there is no need to leak two instance methods: call
and part
. You can simplify it through returning the part from call
.
module Action
class PartsShowBase
#attr_reader :part
def call
find_part # assign @part
reload_part_availability
reload_part_price if @current_user.present?
@part
end
...
end
RSpec.describe PartController, type: :request do
before :all do
allow_any_instance_of(Action::PartsShow).to receive(:call).and_return(returned_part)
end
Reference
https://relishapp.com/rspec/rspec-mocks/v/3-5/docs/working-with-legacy-code/any-instance
How to stub a third party object using rspec-mock?
The code you've included within config.before(:each) do
will execute before each test - no sooner. However, your initializer's code will execute once, before any of the test cases are executed.
This means that instance of Hominid::API
stored in MAILCHIMP
wasn't affected by the instruction to stub Hominid::API.new
- it's still fully functional.
You should place the stub on MAILCHIMP
as well:
config.before(:each) do
allow_any_instance_of(Hominid::API).to receive(:list_subscribe).and_return(true)
allow(MAILCHIMP).to receive(:list_subscribe).and_return(true)
end
How do I mock a class using Rspec and Rails?
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... :)
How to stub a class method using rspec/rspec-mocks
For your workflow, I think it's going to work better to use a class_double
than than to stub the Hashes
class directly. allow(Hashes)
is always going to require that the Hashes
constant is defined. It's simply how Ruby works and RSpec can't do anything about that. With a class double, you can instead do this:
class_double("Hashes", :calculate_hash => canned_return_value).as_stubbed_const
# or
hashes = class_double("Hashes").as_stubbed_const
allow(hashes).to receive(:calculate_hash) do |file|
# look up what to return
end
class_double("Hashes")
provides you with a test double that, when the Hashes
constant is defined, will verify the mocked and stubbed methods against the Hashes
class definition, but when it is not defined, will act just like a normal double that allows anything to be mocked or stubbed on it. The as_stubbed_const
bit tells rspec-mocks to stub the Hashes
constant for the duration of the example so that any references to Hashes
get your class double rather than the real Hashes
class, even if the Hashes
class has never been defined.
Related Topics
Ruby: How to Chain Multiple Method Calls Together with "Send"
How to Dynamically Call Routes Helper in Rails
Ruby, Check If Date Is a Weekend
Installing Ruby 2.0.0 Using Rvm
How to Replace Multiple Newlines in a Row with One Newline Using Ruby
Regex with Named Capture Groups Getting All Matches in Ruby
Difference Between Add_Dependency and Add_Runtime_Dependency
What Is the Preferred Way (Better Style) to Name a Namespace in Ruby? Singular or Plural
Rubymine: Rails Server Launcher Wasn't Found in the Project
Ruby/Rails Using || to Determine Value, Using an Empty String Instead of a Nil Value
How to Return Multiple Tags from a Rails Helper
How to Do a Regex Search in Nokogiri for Text That Matches a Certain Beginning
Rails: Render Doesn't Work, Still Get 'Template Is Missing'
What Does the % Operator Do in Ruby in N % 2
How to Generate the First N Prime Numbers
What Is the Second Parameter/Argument to CSV.Open( ) in Ruby