How to mock a private class method with rspec? - ruby-on-rails

Hey Folks!
I'm researching a way to mock a private class method.
Before I we begin I'd like to give you some context. Firstly I am not doing this on production, initially I tried to mock it (the private class method) and decided against it, partly because I realised it was just wrong to do so and partly because I couldn't figure out how to do it 😁.
On that note some part of me felt that, somewhat ironically, the right way was a cop out 🙈. So now I am trying to satiate the need for knowledge 😋.
Here it goes.
So, I am testing a controller for success (status 200), which depends on the response of a class method in a module maintained by another party. This method in turn does some validation in a private class method contained in that same module. In an attempt to skip over this validation and get straight to the part where I return the data and in the process spare my testing server from incessant creation of Factory models...
I tried to:
allow(TheOtherModule.Class).to receive(:this_private_class_method).and_return(whatever)
To my great disappointment I found such things cannot be achieved like that, allow_any_instance_of was of no help either. This led to some desperate attempts like trying to:
allow(ThisModule.Class).to receive(:send).with('this_private_class_method').and_return(whatever)
...as well as some more elaborate ones like putting a converter of methods in before and after like so:
saved_private_methods = ThisModule.Class.private_methods
before :each do
ThisModule.Class.class_eval { public *saved_private_methods }
end
after :each do
ThisModule.Class.class_eval { private *saved_private_methods }
end
which just failed spectacularly because I copied it and class_eval, somewhat counterintuitively doesn't target class methods, the result of which is that the methods would be found by private_methods...uhm...method but once the conversion was attempted they would not be evaluated, so instead you have to use...
# Oh and dropped this it's overkill
# saved_private_methods = ThisModule.Class.private_methods
before :each do
ThisModule.Class.instance_eval { public :this_private_class_method }
end
after :each do
ThisModule.Class.instance_eval { private :this_private_class_method }
end
...which worked beautifully at what it was supposed to do except that rspec allow still wouldn't pick it up...
So now I am turning to the Bastion of knowledge that is SO in hopes of finding a way it could be done or at least a definite answer of why it cannot.

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

Rails best practices : Public methods in model

I'm a newbie and I'm wondering if my app is going to fail is a near future because I don't understand all subtleties of Rails. So I prefer to ask you :-)
I have a User and a Product model, and I want to create a method that could be used like that :
#user.take!(product)
So I wrote in my User model the following line :
def take!(product)
product.owner = self
end
But if I do that in the private section of my model, it doesn't work. And if I do that in the public section, I don't know if it's recommended. I'm asking myself if it would be better to do something like that in a controller, or in a helper...
Can you enlighten me ?
Writing public methods is fine if they need to be public, it's not a hanging offence or anything like that. The method you describe shouldn't be on the user though - there's no need to put product logic inside the user model, and it's definitely bad to change the product by passing it into a user method. It's also a very short method and so there isn't really very much to be gained by putting it into a method - it just means that if I see take! then I have to go and find where it's defined in order to understand it. You should also only use ! at the end of methods that either might raise an exception or alter the object they are called on.
Putting this logic in the controller would be fine and much clearer, but in general there's nothing wrong with public methods if they can't be private.

Grails service using a method from another service

I'm building a grails app and have come across an issue when trying to instatiate a service, in a different service. Both of the services use a method defined in the other eg.
class fooService{
def barService
barService.doIt()
def getIt(){
...
}
}
class barService{
def fooService
fooService.getIt()
def doIt(){
...
}
}
When I run the app and go to where the methods are used, it brings up this error;
Error creating bean with name 'fooService':
org.springframework.beans.factory.FactoryBeanNotInitializedException: FactoryBean is
not fully initialized yet
Is this something that cannot be done in grails? Or could anyone offer any advice?
Thanks
I've had similar issues in the past, but only when both services are transactional. If it's possible to make at least one of them non-transactional then it should work as-is. If that's not possible then the fallback is to do a kind of "late binding"
class FooService {
def grailsApplication
private getBarService() {
grailsApplication.mainContext.barService
}
public methodThatUsesBarService() {
barService.doSomething()
}
}
This will look up barService in the app context at the point where it is used, rather than at the point where the FooService is created.
Service can be called by another service but not possible at time of initialization. If you want to implement this, the way should be like.
class fooService{
def barService
def getIt(){
...
}
def anotherFooMethod(){
barService.doIt();
}
}
class barService{
def fooService
def doIt(){
...
}
def anotherBarMethod(){
fooService.getIt();
}
}
All these answers are excellent and show how to handle the problem through the framework. Although, when I had this problem I realized there must be a flaw in my plan and architecture if I absolutely must have services calling each other and causing conflicts. Instead of finding a work-around I took a slightly less complicated and strait forward approach -- I restructured. I moved the offending methods from the service class into another service class. It took some refactoring, rethinking and copy/paste skills but I think the application is better for it.
I'm not saying this is better than the other answers. I'm saying, this time, with this project refactoring was a better, faster, less complicated solution. I highly recommend it.
UPDATE
Our final strategy is to refactor all "utility" service functions into a baseService class then have all other services that need the utility services extend baseService. The new policy is to avoid injecting services into other services, unless some situation arises that doesn't allow us to follow this inheritance pattern. This will give us a cleaner code base and less of a spaghetti trail of injections to follow. Plus, it eliminates the emergence of this error.
That's not valid code, so it's hard to know what's really going on. Are the doIt() and getIt() calls in constructors? If so, change the services to implement org.springframework.beans.factory.InitializingBean and do the calls in the afterPropertiesSet method.
You can handle the circular reference using the following -
Lets call it firstSerivce and secondService.
Code changes for secondService class
protected def firstService
def grailsApplication
def initialize() {
this.firstService = grailsApplication.mainContext.firstService
}
code changes in Bootstrap.groovy
def secondService
def init = { servletContext ->
secondService.initialize()
...
..

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

Long method and testing private methods - design problem

I have a quite long method. It copy an ActiveRecord object with all relations, and changes relations in some cases. To make code more readable, I use private methods. I would like to test them. Technicaly, in Ruby it is no problem, but I suspect, I have bad design. Do you have any advices how to deal with such case?
One school of thought is that each and every private method that matters should be tested implicitly by testing a class's public interface. If a private method didn't get called through the public interface, it is redundant. If your private method is that complex to require its own tests, you should consider putting it in a class of its own and test that class.
In short, it shouldn't be necesary to explicitly test your private methods.
as the saying go's: "Don't touch your privates."

Resources