I'm wanting to mock HTTP calls made by Geocoder. I found this post - How can I optionally mock geocoder?
Here is my model
class Listing < ActiveRecord::Base
geocoded_by :address
before_save :geocode, :if => :address_changed?
end
There were two recommendations: VCR (for HTTP requests only?), and Mocha.
I like the looks of Mocha as I can use this for other objects (e.g. models), not just HTTP requests. However, the poster wasn't familiar with Geocoder and didn't know what methods of it to mock, neither do I - still a little new to Rails. Not really sure how/where to trace method calls.
My test looks like this:
test "should create listing" do
sign_in #current_user
assert_difference('Listing.count') do
post :create, listing: valid_params
end
listing = Listing.last
# confirm lat/lng are set
assert_not_nil listing.latitude
assert_not_nil listing.longitude
assert_redirected_to listing_path(assigns(:listing))
end
The test passes, but it can be a little slow to run as it is doing the HTTP request during the test. Would be good to just mock this part in Geocoder.
Alternatively I'll give VCR a go if Geocoder doesn't work with Mocha.
You can stub geocode method on you model it would be enought. For example:
Listing.any_instance.stub(:geocode).and_return([1,1])
Or
allow(Listing).to receive(:geocode).and_return([1,1])
For test unit I guess you should write
Listing.stubs(:geocode).returns([1,1])
Related
I am new with Ruby/Rails and the testing frameworks within Ruby/Rails. I have a pre-validation method (external API) that validates the incoming request. For all test cases, I want to stub that call and test the remaining functionalities.
I am knowledgeable about testing and mocks/stubs/spies (mostly Mockito/Powermockito stuffs), but do not know my way around Rails testing. I tried looking into RSpec / MiniTest stuffs, but it is getting overwhelming.
I have a controller method like this:
def handler
# validate
request_validated = validate_request
unless request_validated
head :unauthorized
return
end
#... remaining codes
end
def validate_request
# validation with external API
end
I have controller tests set up using ActionController::TestCase. Prior to adding the validation stuffs, all my test cases have tested out. But I cannot stub around the validation check.
I would want to have something like
controller.stub(validate_request).then_and_return(true) # need something of this sort
post :handler, as: :json, params: completed_service_parameters
assert_response :no_content
I'm open to using any library, though would prefer to use any Rails in-built, if there's anything. Thanks.
I ended up using 'minitest/stub_any_instance'
require 'minitest/stub_any_instance'
...
test 'test stub' do
...
Controller.stub_any_instance(:function, return-value) do
# perform the call within the stubbed block
post :function, as: :json, params: { :param1 => 'random' }
end
...
end
I am trying to mock Stripe::Charge.retrieve in some of my tests, but am getting the error:
Failure/Error: allow_any_instance_of(Stripe::Charge).to receive(:retrieve)
Stripe::Charge does not implement #retrieve
I am guessing the Stripe API does some sort of reflection to generate this method. Is there a way to mock this method? Or maybe a way to turn off the RSpec feature that verifies that mocked methods exist for specific tests?
Stripe::Charge.retrieve is a class method, but you're trying to mock it like an instance method. The biggest tip-off here is that the method's called "retrieve" - you're trying to get a Stripe::Charge object, and it's more idiomatic in Ruby to use a class method for that. Rspec's error isn't very useful; it would be more accurate if it said "Instances of Stripe::Charge do not implement #retrieve".
The good news is you can mock this in Rspec quite straightforwardly:
allow(Stripe::Charge).to receive(:retrieve)
Here's a short example demonstrating this, taken from a Rails project that includes the Stripe gem and rspec-rails. First, a mostly-empty Payment model:
class Payment < ActiveRecord::Base
def get_stripe_charge(payment_ref)
Stripe::Charge.retrieve(payment_ref)
end
end
And now a spec/models/payment_spec.rb to demonstrate the mocking:
require 'rails_helper'
RSpec.describe Payment, type: :model do
it "Allows retrieval of a payment" do
allow(Stripe::Charge).to receive(:retrieve).and_return("Avocados") # A mock object would be more useful here.
payment = Payment.new
expect(payment.get_stripe_charge("whatever")).to eql("Avocados")
end
end
How can you test the presence of a callback in your model, specifically one that's triggered by creating a record, such as after_create or after_commit on: :create?
Here's an example callback with the (empty) method that it calls.
# app/models/inbound_email.rb
class InboundEmail < ActiveRecord::Base
after_commit :notify_if_spam, on: :create
def notify_if_spam; end
end
Here's the pending spec, using RSpec 3.
# spec/models/inbound_email_spec.rb
describe InboundEmail do
describe "#notify_if_spam" do
it "is called after new record is created"
end
end
Using a message expectation to test that the method is called seems like the way to go.
For example:
expect(FactoryGirl.create(:inbound_email)).to receive(:notify_if_spam)
But that doesn't work. Another way is to test that when a record is created, something inside the called method happens (e.g. email sent, message logged). That implies that the method did get called and therefore the callback is present. However, I find that a sloppy solution since you're really testing something else (e.g. email sent, message logged) so I'm not looking for solutions like that.
I think Frederick Cheung is right. This should work. The problem with your example is that the callback has already been called before the expectation has been set.
describe InboundEmail do
describe "#notify_if_spam" do
it "is called after new record is created" do
ie = FactoryGirl.build(:inbound_email)
expect(ie).to receive(:notify_if_spam)
ie.save!
end
end
end
I'm trying to get my head around RSpec's incredibly confusing, at least initially, syntax by trying to expand on the default specs that are generated with Rails 3 scaffolding...
I have associated models...very simply:
#clown.rb
class Clown < ActiveRecord::Base
has_many :rabbits
end
#rabbit.rb
class Rabbit < ActiveRecord::Base
belongs_to :clown
end
but I'm having trouble with rabbits_controller.spec.rb. In that the specs fail when I reference, say, clown.name in one of rabbit's views. the stupid simple app works as expected but the specs fail because I haven't stubbed (or mocked?) the clown to respond correctly from the rabbit (or at least that's what I think is happening)?!? How should I be adding a stub (or mocking the clown that the rabbit is associate to?).
existing:
#rabbits.controller.spec.rb
require 'spec_helper'
describe RabbitsController do
def mock_rabbit(stubs={})
(#mock_rabbit ||= mock_model(Rabbit).as_null_object).tap do |rabbit|
rabbit.stub(stubs) unless stubs.empty?
end
end
describe "GET index" do
it "assigns all rabbits as #rabbits" do
Rabbit.stub(:all) { [mock_rabbit] }
get :index
assigns(:rabbits).should eq([mock_rabbit])
end
end
...
IMHO (and there are other points of view) this isn't a mocking or stubbing situation. Mocks and stubs are great for external dependencies (think web service), but this is internal to your application. What you want is something like factory_girl, which will let you create test data without the headaches of something like fixtures or trying to mock out every dependent relationship, which quickly becomes monotonous. factory_girl has great documentation, but briefly here's what your factories might look like:
Factory.define(:clown) do |f|
f.rabbits{|c| [c.assocation(:rabbit)]}
f.name "Pierrot"
end
Factory.define(:rabbit) do |f|
f.association :clown
end
You'd then use them in your test like so:
describe RabbitsController do
describe "GET index" do
it "assigns rabbits" do
#rabbit = Factory(:rabbit)
get :index
assigns[:rabbits].should == [#rabbit]
end
end
end
I haven't been able to find anything for a situation like this. I have a model which has a named scope defined thusly:
class Customer < ActiveRecord::Base
# ...
named_scope :active_customers, :conditions => { :active => true }
end
and I'm trying to stub it out in my Controller spec:
# spec/customers_controller_spec.rb
describe CustomersController do
before(:each) do
Customer.stub_chain(:active_customers).and_return(#customers = mock([Customer]))
end
it "should retrieve a list of all customers" do
get :index
response.should be_success
Customer.should_receive(:active_customers).and_return(#customers)
end
end
This is not working and is failing, saying that Customer expects active_customers but received it 0 times. In my actual controller for the Index action I have #customers = Customer.active_customers. What am I missing to get this to work? Sadly, I'm finding that it's easier to just write the code than it is to think of a test/spec and write that since I know what the spec is describing, just not how to tell RSpec what I want to do.
I think there's some confusion when it comes to stubs and message expectations. Message expectations are basically stubs, where you can set the desired canned response, but they also test for the call to be made by the code being tested. In contrast stubs are just canned responses to the method calls. But don't mix a stub with a message expectation on the same method and test or bad things will happen...
Back to your question, there are two things (or more?) that require spec'ing here:
That the CustomersController calls Customer#active_customers when you do a get on index. Doesn't really matter what Customer#active_customers returns in this spec.
That the active_customers named_scope does in fact return customers where the active field is true.
I think that you are trying to do number 1. If so, remove the whole stub and simply set the message expectation in your test:
describe CustomersController do
it "should be successful and call Customer#active_customers" do
Customer.should_receive(:active_customers)
get :index
response.should be_success
end
end
In the above spec you are not testing what it returns. That's OK since that is the intent of the spec (although your spec is too close to implementation as opposed to behavior, but that's a different topic). If you want the call to active_customers to return something in particular, go ahead and add .and_returns(#whatever) to that message expectation. The other part of the story is to test that active_customers works as expected (ie: a model spec that makes the actual call to the DB).
You should have the array around the mock if you want to test that you receive back an array of Customer records like so:
Customer.stub_chain(:active_customers).and_return(#customers = [mock(Customer)])
stub_chain has worked the best for me.
I have a controller calling
ExerciseLog.this_user(current_user).past.all
And I'm able to stub that like this
ExerciseLog.stub_chain(:this_user,:past).and_return(#exercise_logs = [mock(ExerciseLog),mock(ExerciseLog)])