spec'ing private methods - ruby-on-rails

I have some private methods in my rails models. I want to spec it out seperately (use rspec).
I do something like this
class TestModelA < ModelA
def public_wrapper_method_A
private_method_A_from_ModelA
end
end
and I write specs for TestModelA#public_wrapper_method_A
I think there is good case for writing specs for private methods.
What is the best way of writing tests for these private methods.
what do you guys think ? pros/cons?

I usually set aside a context group for private methods and then setup a describes block for each method. Within the block I define a helper method to call the private method and then the it blocks use the call_xxx method to get to the private method.
context "private methods" do
describe "#some_private_method" do
def call_some_private_method
some_obj.send(:some_private_method)
end
it "should return 'something'" do
call_some_private_method.should == 'something'
end
end
end

You don't have to wrap your class to test private methods. You can use send instead.
object.send(:foo_private_method)

Related

RSpec - mock a private class method call inside a request spec?

This is my class
class MyClass
def run
to_be_mocked("arg")
## etc
end
private
def to_be_mocked(arg)
# implementation
end
end
and my Controller, which is what I am writing the request specs for, call this class.
This are my request specs:
context "Some context" do
context "some sub context" do
before :each do
allow(MyClass). to receive(: to_be_mocked).with(account.url).and_return(false)
end
it "responds with a 200" do
do_request
expect(JSON.parse(response.body)["field"]).to eq true
expect(response.status).to eq 200
end
end
However my mocking fails with an MyClass does not implement: to_be_mocked
Already tried removing the private keyword, but got the same results.
What am I missing here?
You're mocking on the class, which is how you mock you "static" class-level methods. For example, if your method was def self.foo and you called it via MyClass.foo, then allow(MyClass) is the way to go.
Your method is not a class-level method, it's an instance method. You invoke it by first creating an instead of MyClass and then calling the method on that instance. You need to use allow_any_instance_of to mock the method for all future instances of the class:
allow_any_instance_of(MyClass).to receive(....)

RSpec - Testing methods that call private methods that should be mocked

I'm using RSpec for testing my classes on Rails.
I'm wondering what is a good way to test methods calling private methods.
For example I have this class:
Class Config
def configuration(overrides)
#config.merge(overrides)
end
private
def read_config_from_yml
#config ||= YAML.load()...
end
end
To test the configuration method, we need to somehow mock the read_config_from_yml method. I know it's not good to simply mock the private method read_config_from_yml or the instance variable #config because that would be messing with the internals of the object.
What I can think of on top of my head:
make read_config_from_yml public
add setter method for config (to avoid mocking the instance variable)
Are these hacks? Any other ideas?
One idea would be to actually create a copy of the YAML file in the test. You could take a snippet of the file that you're using in your production code, write it to the expected file location and delete it upon test completion.
before do
File.open(file_path_here, 'w+') do |f|
f << <<-eof
config:
setting1: 'string'
setting2: 0
eof
end
end
after do
File.delete(file_path_here)
end
it 'does the thing' do
...
end
This would avoid any stubbing and allow you to keep your method private.

Rspec Testing for after_sign_in_path_for(#user) in controller [duplicate]

I got a model with a private method I'd like to spec with RSpec,
how do you usually do ?
Do you only test the method calling the private one ?
or also spec the private one ? if so, how do you do ?
I always take this approach: I want to test the public API my class exposes.
If you have private methods, you only call them from the public methods you expose to other classes. Hence, if you test that those public methods work as expected under all conditions, you have also proven that the private methods they use work as well.
I'll admit that I've come across some especially complex private methods. In that extreme case you want to test them, you can do this:
#obj.send(:private_method)
For the private methods that need code coverage (either temporarily or permanently), use the rspec-context-private gem to temporarily make private methods public within a context.
gem 'rspec-context-private'
It works by adding a shared context to your project.
RSpec.shared_context 'private', private: true do
before :all do
described_class.class_eval do
#original_private_instance_methods = private_instance_methods
public *#original_private_instance_methods
end
end
after :all do
described_class.class_eval do
private *#original_private_instance_methods
end
end
end
Then, if you pass :private as metadata to a describe block, the private methods will be public within that context.
class Example
private def foo
'bar'
end
end
describe Example, :private do
it 'can test private methods' do
expect(subject.foo).not eq 'bar'
end
end
create a dummy class and access private method using
.send(:private_method, args)
example
obj = Class.new { extend Classname } obj.send(:sum, 1,2)
obj = Class.new { extend Classname } obj.send(:sum)
If you're wanting to test an expectation on a private method, the accepted answer won't really work (at least not that I know of, so I'm open to correction on that point). What I've done instead is even filthier - in the test itself, just expose the method by redefining it:
def object_to_test.my_private_method
super
end
Works on Ruby 1.8, can't comment on any of the newer runtimes.

Rails ActiveSuppport:Concern and Private Methods

This is a great idea about concern in rails: http://37signals.com/svn/posts/3372-put-chubby-models-on-a-diet-with-concerns
And it's also a good idea to make very small methods that are not part of a public API. Without using concerns, those become private methods in a ruby class.
Does it makes sense to create private methods inside of a Rails ActiveSupport::Concern module? If so, does private work both for regular instance methods and class methods in the concern definition?
Does it makes sense to create private methods inside of a Rails ActiveSupport::Concern module?
Considering that concerns are smart modules that will eventually be included in other classes — yes, it does. It's just a portable code, extractable behavior and I'd like to consider it as part of my controller (or model, etc.) as I'm writing it. So basically you just declare methods private or protected as you normally would.
Maybe the post you linked have been updated since 2013, but DHH does exactly that in the one of the examples there:
module Dropboxed
extend ActiveSupport::Concern
included do
before_create :generate_dropbox_key
end
def rekey_dropbox
generate_dropbox_key
save!
end
private # <- Let's list some privates
def generate_dropbox_key
self.dropbox_key = SignalId::Token.unique(24) do |key|
self.class.find_by_dropbox_key(key)
end
end
end
As to private class methods, I agree with #Hugo and never used them myself, but here's how you can achieve this:
module Dropboxed
extend ActiveSupport::Concern
included do
private_class_method :method_name
end
module ClassMethods
def method_name
end
end
end
It's just my opinion but right now I'm scratching my head about private class method, what are they good for? Anyway, if you really need them refer to this post: How to create a private class method?
It does make sense to have private instance methods in a concern module and will work fine. Private class methods will work fine as well but following the above stated post.

Calling protected class method from instance method in Ruby

I've been having this bothering recurring theme; let's just say, I have a class which defines an instance method and a protected class method. The instance method must call the class method. In order to do so, I kind of have to break the visibility rule and use the dangerous 'send' function. Something like this:
class Bang
def instance_bang
self.class.send(:class_band)
end
protected
def self.class_bang
puts "bang"
end
end
I find this awful, since the class method should be used inside the class scope, therefore should remain visible and callable within it, right? Is there an alternative way to use class methods in instance methods with needing to rely on the "send" function and therefore not break visibility?
UPDATE:
Following Sergio Tulentsev's response (thx for the correction), I'll update my concern with a code snippet that sums up my concerns of the method visibility being taken into account while still inside the scope where it has been defined.
class Bang
def instance_bang
private_bang = 1
self.private_bang(private_bang)
end
private
def private_bang(p)
puts "bang"
p
end
end
Calling Bang.new.instance_bang will raise an Exception unless you use send on that private_bang call (this time I checked it :) ).
EDIT: Answering the updated question
It is forbidden to call private methods with explicit receiver. You either have to use implicit receiver (private_bang, without self) or use send. Please see my another answer for more information.
By the way, the original question is about calling class instance methods from instance methods. Your clarification doesn't include that. But if that's still true, you have to use self.class.send or make the method public (so that you can use explicit receiver).
Let's consider a private class method (since protected class methods don't make sense).
We know it's possible for an instance to call a private method on itself, as long as it isn't using an explicit receiver (self.call_something_private). It seems you also expect that an instance can call a private class method on its own class, but that is not the case.
Let's look at a way to do this without using send.
The private and protected macros only affect instance methods of the current scope, not class methods. Here are three ways to rewrite your original code:
class Bang
def instance_bang
self.class.class_bang
end
# declare method visibility after
def self.class_bang
puts "bang"
end
private_class_method :class_bang
# inline
private_class_method def self.class_bang
puts "bang"
end
# class scope
class << self
# the private macro works here because we're inside the class scope
private
def class_bang
puts "bang"
end
end
end
So now we want to expose an interface on the class to call class_bang, but only if it's called by an instance of Bang.
class Bang
def instance_bang
self.class.invoke_class_bang(self)
end
class << self
private
def class_bang
puts "bang"
end
public
# we ask the receiver to pass itself as an argument ...
def invoke_class_bang(receiver)
# ... so that we can check whether it's
class_bang if receiver.is_a?(Bang)
end
end
end
That's not a very nice looking solution though. Here's a sneakier way:
class Bang
def initialize
def self.instance_bang() self.class.method(:class_bang).call end
end
class << self
private
def class_bang
puts "bang"
end
end
end
"The class method should be used inside the class scope, therefore should remain visible and callable within it, right?" Yes, that's correct, and that's the behavior Ruby exhibits. (As a point of clarification, instance scope is not "within" class scope. They are, appropriately, separate.)
The non-send solution is to subclass or reopen the class to add a public class method to access the protected class method.

Resources