RSpec mock service and corresponding method - ruby-on-rails

I want to mock a service that returns the integer 0 when called with get_auth_key.api_response_code.
This is what I have so far:
before(:each) do
PolicyObjects::BpointRequestPolicy.any_instance.stub(:get_auth_key).and_return(OpenStruct.new(api_response_code: 0))
end
I would like to avoid creating a class for this in the test as I believe it's overkill. Somewhere in the codebase, response = PolicyObjects::BpointRequestPolicy.new(...) gets called and then compared: if response.api_response_code == 0. I would like it to return 0 to hit conditionals in my test.
What is the best practice or easiest way of doing this?

This is a great use case for a double or better an instance_double if you know the class you're mocking out:
let(:api_resonse) { instance_double("SomeResponseClass", api_response_code: 0) }
before(:each) do
PolicyObjects::BpointRequestPolicy.any_instance.stub(:get_auth_key).and_return(api_response)
end

Related

RSpec: How can I not use `allow_any_instance_of` for objects that get instantiated in the functions I call?

I have a class A with a method M for which I want to write a test T. The problem is that method M creates a new object O. I want to mock a method F of that new object O.
class A
def M(p1, p2)
#o = O.new(p1, p2)
end
end
class O
def F(q)
...
end
end
I can very easily do so with the allow_any_instance_of feature of RSpec, but I really don't see a way of doing so with just allow or expect. I understand that I can mock a method of an existing instance and of a class but from my tests I couldn't make it work against methods of objects that get created in a method I'm testing.
T :process do
it "works" do
# This works
allow_any_instance_of(O).to receive(:F).and_return(123)
...
end
it "does not works" do
# This fails
allow(O).to receive(:F).and_return(123)
...
end
end
How do I know that it fails?
I changed my F method with a puts() and I can see that output on the screen when I use the allow(O). It does not appear at all when I use the allow_any_instance_of(). So I know that it's working as expected only in the latter.
def F(q)
puts("If I see this, then F() was not mocked properly.")
...
end
I would think that allow(O)... should connect to the class so whenever a new instance is created the mocked functions follow, but apparently not.
Do you have RSpec tests handling such mocking cases in a different way that would not involve the use of the allow_any_instance_of() function?
The reason I ask is because it is marked as obsolete (#allow-old-syntax) since RSpec 3.3 so it sounds like we should not be using this feature anymore, especially once RSpec 4.x comes out, it probably will be gone.
The reason this
allow(O).to receive(:F).and_return(123)
Doesn't work is that :F is not a method of O, so the O never receives this message (method invocation).
The best solution for you would be to refactor your code to use dependency injection. (Please note that your example is abstract to the extreme, if you provided a real life example - closer to the ground - some better refactoring might be possible)
class A
attr_accessor :o_implementation
def initialize(o_implementation)
#o_implementation = o_implementation
end
def M(p1, p2)
#o = o_implementation.new(p1, p2)
end
end
RSpec.describe A do
subject { described_class.new(klass) }
let(:klass) { O }
let(:a_double) { instance_double(klass) }
it do
allow(klass).to receive(:new).and_return(a_mock)
allow(a_double).to receive(:F).and_return(123)
end
end
With the Dependency injection you move outside the decision which class to instantiate. This decouples your code (A stops being coupled to O, now it depends only on the O interface that it's using), and makes it easier* to test.
(*) One could argue that allow_any_instance is easier (less involved, less typing), but it has some issues, and should be avoided if possible.
(as a small aside: I can understand the probable need for very thorough anonymization of your code, but you could still follow ruby style guide: methods start with lower-case, only classes start with upper-case)
So first off: allow(O) works, but will only capture class methods. If you need to capture instance methods, you need to call allow for a specific instance.
Since your example is pretty sparse, I see no reason why we could not split up the creation of the object from the test? If that is possible, a very simple approach would be to write something like:
describe :process do
before do
#o = A.o_maker(p1,p2)
allow(#o).to receive(:some_function) { 123 }
end
it "works" do
# do something with `#o` that should call the function
end
end
I personally prefer this approach over creating the mock class, as suggested before.
This is probably well known, but for clarity: the problem with a mock class imho is that you are no longer testing class A but the mock. This could in some cases be useful, but from the original question it is unclear if it applies in this case and if this is not needlessly complicated. And secondly: if your code is this complicated (e.g. some method that creates a new object and then calls F), I would rather 1) refactor my code to make it test-able, and/or 2) test side effects (e.g. F adds an audit-log-line, sets a state, ...). I do not need to "test" my implementation (is the correct method called), but is it performed (and of course, as always, there are exceptions e.g. when calling external services or something --but again all that is impossible to deduce from the original question).

Testing factory methods iOS

What approach should I take to writing unit test for factories that create other objects? I've seen tests that would simply initialize two PeriodicNotifications and assert that they are equal objects. Perhaps also compare PeriodicNotification properties to the notification properties to test that PeriodicNotification parsed correctly?
They main challenge I face is knowing WHAT to test for.
The scope of a unit test can be quite modest, pretty much limited to the intent of the thing being tested. For an instance-returning class method, the unit test questions is: did I get an instance of the class initialized as I expect it to be?
The test in the OP case might be:
PumpNotification *pumpNotification = // stub object
PeriodicNotification *periodicNotification = [PeriodicNotification nodeFromNotification: pumpNotification];
// do I have an instance?
assert([periodicNotification isMemberOfClass:[PeriodicNotification self]])
assert([node[kJSON_type] isEqual:/*value from stub object*/])
// and so on for other values

How can I simply test a method static method call using Minitest?

The class I am testing has a method:
def configuration_data(organization_id, plan_year_id)
Benefit::BenefitConfiguration.new(organization_id, plan_year_id).index
end
I already have a separate test for the BenefitConfiguration index method, so in this case I would like to have code coverage for the configuration_data method. Not sure what the best way to test the method to achieve code coverage is in this case.
My attempt at a test is:
instance = Benefit.new
index_mock = MiniTest::Mock.new
index_mock.expect :index, true
Benefit::BenefitConfiguration.any_instance.stubs(:new).returns(index_mock)
result = instance.configuration_data(1, 2)
assert_equal result, true
But when run, it fails inside of the actual BenefitConfiguration index method, it doesn't seem to be using the mock index return value of true.
Any help would be appreciated!
Benefit::BenefitConfiguration.any_instance.stubs(:new)
any_instance gets triggered when the new method is called on the given class... so this will match when you do something like:
Benefit::BenefitConfiguration.new
However... you are then telling it to stub over the new method on an instance of your class... so you are saying you are looking for code that does this:
Benefit::BenefitConfiguration.new.new
which isn't what you really want.
Probably you want something like:
Benefit::BenefitConfiguration.stubs(:new).returns(index_mock)

RSpec Stubbing Service Object Method

I'm having an issue with stubbing out a call to a service object (QuickbooksService) from an AR object. As far as I can tell the method should be stubbed properly and return the value I'm specifying but when I run the spec I can see the method being called and failing.
Here's the class definition of the object I'm testing.
class Order < ActiveRecord::Base
def create_invoice
QuickbooksService.new(:estimate).create_invoice(arg1, arg2, arg3)
end
end
And from order_spec
describe("#create_invoice") do
expect(QuickbooksService.new(:estimate)).to receive(:create_invoice).and_return(1)
end
I've also tried
allow(QuickbooksService.new(:estimate)).to receive(:form_invoice_create).with(anything()).and_return(1)
So instead of returning 1 the create_invoice method is being executed inside of QuickbooksService. Any insight would be appreciated!
The problem you are having is that you are stubbing a seperate instance. i.e When you define the expectation, you telling it to expect that a particular instance receives a call to the method, but when the code is executed, it is creating a different instance. What you need to do is allow any instance to receive the method call. Something like allow_any_instance_of(QuickbooksService).to receive(:invoice_create) will work, but it is much better practice to create a double, something like:
let(:quickbooks_service) { instance_double(QuickbooksService) }
describe("#create_invoice") do
before { allow(quickbooks_service).to receive(:create_invoice).and_return(1) }
it "Creates quickbook invoice" do
order.create_invoice
expect(quickbooks_service).to have_received(:create_invoice)
end
end
See: https://relishapp.com/rspec/rspec-mocks/docs
The problem is that you are instantiating the class while stubbing it allow(QuickbooksService.new(:estimate)).to receive(:form_invoice_create).with(anything()).and_return(1)
Try this:
allow_any_instance_of(QuickbooksService).to receive(:form_invoice_create).with(anything()).and_return(1)

Proper technique when passing data between methods

If you have two methods in a model or controller and you want to pass a variable between methods e.g.
def foo
#param = 2
#test = 1
callee
#do something with #test
end
def callee
#test += #param
end
Is it better to use instance variables to do this or regular variables like so
def foo
param = 2
test = 1
test = callee(param, test)
#do something with test
end
def callee(param, test)
test += param
test
end
Thanks in advance!
There isn't a definite answer to this question, it depends a lot on the context - the thing you need to ask is "which approach best demonstrates the intent of the code". You should definitely have tests for the model/controller class you are talking about.
As a very rough guideline:
The first approach is commonly seen when the method is part of the class's public API and it alters the internal state of instances of the class (although it may be the sign of a code smell if you have public methods chained as in your example.) This is probably going to be seen more often in a model object.
The second approach is usually seen when the method you are calling is a private convenience method that factors out some code duplication, or a method which does very specialised operations on the parameters and returns some result (in which case it should probably be factored out into a utility class.) This may be seen in model or controller objects.
If you are aiming for maintainable OO code, then the principles of SOLID design are very good guidelines - have a look at Uncle Bob's article about them here:
http://blog.objectmentor.com/articles/2009/02/12/getting-a-solid-start
It depends on your needs. Also, prototype of the function that you are passing variables to is also important. If you want the method not to change any of the parameters without your permission, you should use your second implementation. But, if you trust the function, you can use first method. This is a big topic called as "call by reference" and "call by value". You can examine following link;
http://www.exforsys.com/tutorials/c-language/call-by-value-and-call-by-reference.html

Resources