Stubbing a mock object does not work - spock

I have a factory class carfactory that i create a mock object of.
def mockCar = Mock(Car)
def mockFactory = Mock(CarFactory)
this.mockCar.getDoors(_) >> ["a","b"]
this.mockFactory.getCar(_) >> this.mockCar
Now when the test runs i get a mockCar object from the factory but not the set from the getDoors(_) call.
I get a empty set.

The usage of this. is incorrect because mockCar and mockFactory are local variables, not instance fields. After fixing this, the shown code works fine (I just double-checked in Spock Web Console). If you get back an empty set, chances are that your real code has another problem not shown here.

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).

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)

How to get a query to return my mock object?

If in my RSpec test I have
foo = mock_model(Foo, id: 1)
& my code runs
Foo.where(id: 1)
My code will always fail as the mock_model foo never gets to belong to the Foo collection, e.g.
Foo.count # => 0
If I run foo.instance_of?(Foo) I get true so the behaviour is right. What am I missing? I'd like to rely on true mocks & not have to rely on FactoryGirl or instantiate an object of Foo itself.
Firstly, well done on not using FactoryGirl. Your tests will become faster as you are reducing the number of times your tests hit your database. But this approach will need perseverance.
Now, once you use mock_model you will need to stub all of the methods that are called on your mock.
This means you will not be able to call Foo.where(id: 1), as this is a model object that will not contain your newly created foo. Remember foo is a mocked object, which is not written to your database.
So the 'anomaly' you are getting is not an anomaly. It is telling you that your mocked object foo, has a form Foo, which of course it does. But your mock is not saved to the database. Hence Foo.count returns 0.
To finish your test you need to finish off your assertion by saying something along these lines
allow(Foo).to receive(where).with(id: 1).and_return(foo)
I hope this helps

How can I get access to the class instance that is rendering my view in a test?

I'm trying to test a rake task which scrapes my site and pushes the content to an elasticsearch server; the task works fine. However the test is failing because in one view I randomly pick some values like this:
[:breast,:ovarian][rand(2)]
(rand * 4)-2
rand(Date.new(2006)..Time.now.to_date)
Which means I need to stub rand. In order to stub rand I need access to the class-instance that is calling it, which in this case is whatever class is rendering my view. Calling puts self.class Just returns Class and an id, so how can I get ahold of the instance in order to stub it?
I could pass these values into the view from the controller as instance variables, if getting ahold of the controller would be easier.
long story short: it would be better to extract the offending logic and place it in a helper. This way you will be able to stub it easily, and even unit-test it if needed. Moreover this improves the overall quality of your code (no logic should belong to the view).
also, rand is a method from Kernel, so it is already "stubable"

Resources