How to avoid using allow_any_instance_of? - ruby

Imagine we have following piece of code:
class A
def create_server
options = {
name: NameBuilder.new.build_name
}
do_some_operations(options)
end
end
To test such methods, I've used to use allow_any_instance_of:
it 'does operations' do
allow_any_instance_of(NameBuilder).to receive(:build_name)
# test body
end
But docs advise us not to use it because of several reasons. How then avoid allow_any_instance_of? I've came to only one solution:
class A
def create_server
options = {
name: builder.build_name
}
do_some_operations
end
private
def builder
NameBuilder.new
end
end
But with such approach code quickly becomes full of almost useless methods (especially when you actively using composition of different objects in described class).

In the absence of dependency injection as per Uzbekjon's answer (which I agree with) you could also consider stubbing out the call to NameBuilder.new so you can have direct control of the instance of NameBuilder under test:
class NameBuilder
def build_name
# builds name...
end
end
class A
def create_server
options = {
name: NameBuilder.new.build_name
}
do_some_operations(options)
end
def do_some_operations(options)
# does some operations
end
end
RSpec.describe A do
let(:a) { described_class.new }
describe '#create_server' do
let(:name_builder) { instance_double(NameBuilder) }
before do
allow(NameBuilder).to receive(:new).and_return(name_builder)
end
it 'does operations' do
# the first expectation isn't really part of what you seem
# to want to test, but it shows that this way of testing can work
expect(name_builder).to receive(:build_name)
expect(a).to receive(:do_some_operations)
a.create_server
end
end
end

If it is difficult to test, it means you have a problem in your class design. In your case, when you are doing testing for specific method call on a specific class within a class you are testing like this:
allow_any_instance_of(NameBuilder).to receive(:build_name)
Your test know exactly how the method is implemented internally. Your classes should encapsulate the logic and hide it. You are doing exactly the opposite.
You should not be testing any internal method logic. Just test the behaviour. Give inputs and test the correctness of the output.
If you really want to test that method call on NameBuilder class, then inject that dependency and make your class more testable. This also follows OOP principles.
class A
def create_server(builder)
do_some_operations(name: builder.build_name)
end
end

Related

Testing an abstracted if conditional

I'm trying to figure out the best way to test find_communities here without resorting to using polymorphism here to defeat the if statement staring at me.
class CommunityFinder
def initialize(filters={})
#filters = filters
end
def find_communities
return my_communities if #filters[:my_communities]
visible_communities
end
def my_communities
# [...]
end
def visibile_communities
# [...]
end
end
I have both my_communities and visible_communities well tested, but I have concerns about testing find_communities.
I don't want to duplicate the test setup for both my_communities and visible_communities, because there's likely going to be
I would prefer for the class API to contain all 3 public methods because the conditions for find_communities won't ever change.
I'm writing this with the expectation that the class is going to change by someone other than me in the near future, and that there's going to be more methods
Should I:
make find_communities live in the caller
make find_communities be it's own strategy
duplicate the tests into find_communities
pick your own 4th option.
This example is a case where you really should have two subclasses, each of which implements its own communities method:
class CommunityFinder::Base
def initialize(**options)
#options = options
end
end
class CommunityFinder::Mine < CommunityFinder::Base
def communities
end
end
class CommunityFinder::Visible < CommunityFinder::Base
def communities
end
end
You can use a factory method to instantiate the correct subclass:
module CommunityFinder
def self.filter(**options)
if (options[:my_communities])
CommunityFinder::Mine.new(options)
else
CommunityFinder::Visible.new(options)
end
end
end

Testing Ruby Modules with rspec

I'm having a bad time finding on SO/Google this particular case. I have a module with functions and in order to use them, you have to create a Class which includes/extends the Module depending if you'll want instance methods or class methods.
module A
def say_hello name
"hello #{name}"
end
def say_bye
"bye"
end
end
How can I test this module using rspec?
I have something like this, and I'm not sure where is the point I should create the class and extend Module.
describe A do
class MyClass
extend A
end
before(:each) { #name = "Radu" }
describe "#say_hello" do
it "should greet a name" do
expect(Myclass.say_hello(#name)).to eq "hello Radu"
end
end
end
Thank you!
You can create an anonymous class in your tests:
describe A do
let(:extended_class) { Class.new { extend A } }
let(:including_class) { Class.new { include A } }
it "works" do
# do stuff with extended_class.say_hello
# do stuff with including_class.new.say_hello
end
end
To see something similar in real code, I've used this strategy for testing my attr_extras lib.
That said, include and extend are standard features of Ruby, so I wouldn't test that every module works both when including and when extending – that's usually a given.
If you create a named class in the test, like you do in your question, I believe that class will exist globally for the duration of your test run. So this class will leak between every test of your test suite, potentially causing conflicts somewhere.
If you use let to create an anonymous class, it will only be available inside this particular test. There is no global constant pointing to it that could conflict with other tests.
You could also use RSpec's stub_const to get a constant that doesn't leak, if you need to:
stub_const("MyClass", Class.new { … })
# do stuff with MyClass
Note that you'd run stub_const inside a before or it. Not just at the top of the file or in the class context.
To help out future readers, here's an example I got going using #henrik-n 's solution:
# slim_helpers.rb
module SlimHelpers
# resourceToTitle converts strings like 'AWS::AutoScaling::AutoScalingGroup'
# to 'Auto Scaling Group'
def resourceToTitle(input)
input.split('::')[-1].gsub(/([A-Z])/, ' \1').lstrip
end
end
# slim_helpers_spec.rb
require_relative '../slim_helpers'
describe SlimHelpers do
# extended class
let(:ec) { Class.new { extend SlimHelpers } }
it "converts AWS resource strings to titles" do
out = ec.resourceToTitle('AWS::AutoScaling::AutoScalingGroup')
expect(out).to eq 'Auto Scaling Group'
end
end

Better way to turn a ruby class into a module than using refinements?

Module#refine method takes a class and a block and returns a refinement module, so I thought I could define:
class Class
def include_refined(klass)
_refinement = Module.new do
include refine(klass) {
yield if block_given?
}
end
self.send :include, _refinement
end
end
and the following test passes
class Base
def foo
"foo"
end
end
class Receiver
include_refined(Base) {
def foo
"refined " + super
end
}
end
describe Receiver do
it { should respond_to(:foo) }
its(:foo) { should eq("refined foo") }
end
So, using refinements, I can turn a class into a module, refine its behaviour on the fly, and include it in other classes.
Is there a simpler way to turn a class into a module in Ruby (say in ruby < 2)?
In the C-implementation of rb_mod_refine
we see
refinement = rb_module_new();
RCLASS_SET_SUPER(refinement, klass);
Is this just setting the superclass of refinement to klass that copies the implementation of the class inside the refinement module?
I am aware that multiple inheritance IS
done via Modules, but what would the community think of the above Class#include_refined?
Would it be reasonable to extract this aspect out of refinements?
"Locally" patching inside a Class instead of using "using" switches to activate refinements?
I am happy indeed with Ruby 2.1 (and later) class-level "private" scope of refinements. My example above can be rephrased as:
# spec/modulify_spec.rb
module Modulify
refine(Class) do
def include_refined(klass)
_refined = Module.new do
include refine(klass) { yield if block_given? }
end
include _refined
end
end
end
class A
def a
"I am an 'a'"
end
end
class B
using Modulify
include_refined(A) do
def a
super + " and not a 'b'"
end
end
def b
"I cannot say: " + a
end
end
RSpec.describe B do
it "can use refined methods from A" do
expect(subject.b).to eq "I cannot say: I am an 'a' and not a 'b'"
end
end
and suits as solution for the original problem.
Andrea, thank you for the info in comment. Excuse my lack of knowledge to understand this is really necessary though it sounds doable as per your research.
I don't think we need to go so low level to do something in Rails.
If I'm going to do similar on Engine, I will try the following ideas, from easy to hard.
In routes.rb, mount the whole engine in right route.
I'm afraid this most common usage can't fit your need
In routes.rb, Customize engine's route for specific controllers in application route.
Devise, as an engine, can do easily. But I know not every engine could do this.
In routes.rb, redirect specific or whole set of routes to engine's routes
In your application's action, redirect to specific engine's action in application's action.
This should be customized enough for specific action
class FoosController < ApplicationController
def foo
redirect_to some_engine_path if params[:foo] == 'bar'
end
Inherit the engine's controller - for a set of actions, and if all above can't fit
*The engine's classes are available in all application, you can inherit a controller from them, instead of normal ApplicationController.
# class FoosController < ApplicationController
class FoosController < BarEngine::BarsController
*Since most engine's controller inherit from ApplicationController, this inheritance still allows you to use your own things from ApplicationController, no bad effect at all.
If all above can't do, I can try to serve a customized locally or from my github repo.
In conclusion, the above should be able to solve most of cases, and I myself prefer #5 when possible and needed.

How do I switch implementation at instantiation time?

I have a class in Ruby that holds some stuff, I'll call FooBox:
class FooBox
...
end
I have two possible backing-data stores for FooBox called BoxA and BoxB with different characteristics but the same interface:
class BoxA
include Enumerable
def put_stuff(thing)
...
end
end
class BoxB
include Enumerable
def put_stuff(thing)
...
end
end
How can I instantiate a FooBox, and, based on a parameter, decide whether to back it with a BoxA or BoxB implementation? I do not want to pass in the implementation into the constructor; I only want to pass something to determine which kind to use.
class FooBox
def initialize(implementation_choice)
# ???
end
end
I usually do something like this:
class BoxA
def self.match? options
# figure out if BoxA can be used given options
end
end
# Implement BoxB (and other strategies) similarly to BoxA
class FooBox
STRATEGIES = [BoxA, BoxB]
def initialize options
#options = options
end
def strategy
#strategy ||= STRATEGIES.detect { |strategy| strategy.match? #options }
end
end
This keeps the responsibility of “knowing” if the strategy is able to be used within the strategy itself (rather than making the context class monolithic), and then just picks the first one in the list that says it can work.
I’ve used this pattern (and similar variations for slightly different problems) several times and have found it very clean.
The simple solution is create a mapping for the strategy's type and strategy class, just like #Andrew Marshall's solution
But to be better I would considering two things:
The strategies' holder (here is the FooxBox ) now need to know every box implenentation, and hard-coding their names to itself; this is not a flexiable
approach, considering one day you want to add another strategy, go to the code and add it? With ruby we can do it with a 'self registering' easily.
You don't want to strategies holder will return implementation wildly, I mean both 'BoxA' and 'BoxB' or someday's 'BoxXYZ' should belong to same strategy
concept, in Java, it maybe means all of them should implemente an interface, with ruby we generally do it with include SomeMoudle
In my application I use the following solution(just demo)
module Strategies
def self.strategies
##strategies ||= {}
end
def self.strategy_for(strategy_name)
##strategies[strategy_name]
end
end
module Strategy
def self.included(base)
base.class_eval do
def self.strategy_as(strategy_name)
Strategies.strategies[strategy_name] = self
end
end
end
end
class BoxA
include Strategy
strategy_as :box_a
def do_stuff
puts "do stuff in BoxA"
end
end
class BoxB
include Strategy
strategy_as :box_b
def do_stuff
p "do stuff in BoxB"
end
end
## test
Strategies.strategy_for(:box_a).new.do_stuff
Strategies.strategy_for(:box_b).new.do_stuff
If you want to detect strategy with match block, you can change strategy_as to accept a block. then use Strategies.strategy_for{...}.new.do_stuff

What's the best way to unit test protected & private methods in Ruby?

What's the best way to unit test protected and private methods in Ruby, using the standard Ruby Test::Unit framework?
I'm sure somebody will pipe up and dogmatically assert that "you should only unit test public methods; if it needs unit testing, it shouldn't be a protected or private method", but I'm not really interested in debating that. I've got several methods that are protected or private for good and valid reasons, these private/protected methods are moderately complex, and the public methods in the class depend upon these protected/private methods functioning correctly, therefore I need a way to test the protected/private methods.
One more thing... I generally put all the methods for a given class in one file, and the unit tests for that class in another file. Ideally, I'd like all the magic to implement this "unit test of protected and private methods" functionality into the unit test file, not the main source file, in order to keep the main source file as simple and straightforward as possible.
You can bypass encapsulation with the send method:
myobject.send(:method_name, args)
This is a 'feature' of Ruby. :)
There was internal debate during Ruby 1.9 development which considered having send respect privacy and send! ignore it, but in the end nothing changed in Ruby 1.9. Ignore the comments below discussing send! and breaking things.
Here's one easy way if you use RSpec:
before(:each) do
MyClass.send(:public, *MyClass.protected_instance_methods)
end
Just reopen the class in your test file, and redefine the method or methods as public. You don't have to redefine the guts of the method itself, just pass the symbol into the public call.
If you original class is defined like this:
class MyClass
private
def foo
true
end
end
In you test file, just do something like this:
class MyClass
public :foo
end
You can pass multiple symbols to public if you want to expose more private methods.
public :foo, :bar
instance_eval() might help:
--------------------------------------------------- Object#instance_eval
obj.instance_eval(string [, filename [, lineno]] ) => obj
obj.instance_eval {| | block } => obj
------------------------------------------------------------------------
Evaluates a string containing Ruby source code, or the given
block, within the context of the receiver (obj). In order to set
the context, the variable self is set to obj while the code is
executing, giving the code access to obj's instance variables. In
the version of instance_eval that takes a String, the optional
second and third parameters supply a filename and starting line
number that are used when reporting compilation errors.
class Klass
def initialize
#secret = 99
end
end
k = Klass.new
k.instance_eval { #secret } #=> 99
You can use it to access private methods and instance variables directly.
You could also consider using send(), which will also give you access to private and protected methods (like James Baker suggested)
Alternatively, you could modify the metaclass of your test object to make the private/protected methods public just for that object.
test_obj.a_private_method(...) #=> raises NoMethodError
test_obj.a_protected_method(...) #=> raises NoMethodError
class << test_obj
public :a_private_method, :a_protected_method
end
test_obj.a_private_method(...) # executes
test_obj.a_protected_method(...) # executes
other_test_obj = test.obj.class.new
other_test_obj.a_private_method(...) #=> raises NoMethodError
other_test_obj.a_protected_method(...) #=> raises NoMethodError
This will let you call these methods without affecting other objects of that class.
You could reopen the class within your test directory and make them public for all the
instances within your test code, but that might affect your test of the public interface.
One way I've done it in the past is:
class foo
def public_method
private_method
end
private unless 'test' == Rails.env
def private_method
'private'
end
end
I'm sure somebody will pipe up and
dogmatically assert that "you should
only unit test public methods; if it
needs unit testing, it shouldn't be a
protected or private method", but I'm
not really interested in debating
that.
You could also refactor those into a new object in which those methods are public, and delegate to them privately in the original class. This will allow you to test the methods without magic metaruby in your specs while yet keeping them private.
I've got several methods that are
protected or private for good and
valid reasons
What are those valid reasons? Other OOP languages can get away without private methods at all (smalltalk comes to mind - where private methods only exist as a convention).
Similar to #WillSargent's response, here's what I've used in a describe block for the special case of testing some protected validators without needing to go through the heavyweight process of creating/updating them with FactoryGirl (and you could use private_instance_methods similarly):
describe "protected custom `validates` methods" do
# Test these methods directly to avoid needing FactoryGirl.create
# to trigger before_create, etc.
before(:all) do
#protected_methods = MyClass.protected_instance_methods
MyClass.send(:public, *#protected_methods)
end
after(:all) do
MyClass.send(:protected, *#protected_methods)
#protected_methods = nil
end
# ...do some tests...
end
To make public all protected and private method for the described class, you can add the following to your spec_helper.rb and not having to touch any of your spec files.
RSpec.configure do |config|
config.before(:each) do
described_class.send(:public, *described_class.protected_instance_methods)
described_class.send(:public, *described_class.private_instance_methods)
end
end
You can "reopen" the class and provide a new method that delegates to the private one:
class Foo
private
def bar; puts "Oi! how did you reach me??"; end
end
# and then
class Foo
def ah_hah; bar; end
end
# then
Foo.new.ah_hah
I would probably lean toward using instance_eval(). Before I knew about instance_eval(), however, I would create a derived class in my unit test file. I would then set the private method(s) to be public.
In the example below, the build_year_range method is private in the PublicationSearch::ISIQuery class. Deriving a new class just for testing purposes allows me to set a method(s) to be public and, therefore, directly testable. Likewise, the derived class exposes an instance variable called 'result' that was previously not exposed.
# A derived class useful for testing.
class MockISIQuery < PublicationSearch::ISIQuery
attr_accessor :result
public :build_year_range
end
In my unit test I have a test case which instantiates the MockISIQuery class and directly tests the build_year_range() method.
In Test::Unit framework can write,
MyClass.send(:public, :method_name)
Here "method_name" is private method.
& while calling this method can write,
assert_equal expected, MyClass.instance.method_name(params)
Here is a general addition to Class which I use. It's a bit more shotgun than only making public the method you are testing, but in most cases it doesn't matter, and it's much more readable.
class Class
def publicize_methods
saved_private_instance_methods = self.private_instance_methods
self.class_eval { public *saved_private_instance_methods }
begin
yield
ensure
self.class_eval { private *saved_private_instance_methods }
end
end
end
MyClass.publicize_methods do
assert_equal 10, MyClass.new.secret_private_method
end
Using send to access protected/private methods is broken in 1.9, so is not a recommended solution.
To correct the top answer above: in Ruby 1.9.1, it's Object#send that sends all the messages, and Object#public_send that respects privacy.
Instead of obj.send you can use a singleton method. It’s 3 more lines of code in your
test class and requires no changes in the actual code to be tested.
def obj.my_private_method_publicly (*args)
my_private_method(*args)
end
In the test cases you then use my_private_method_publicly whenever you want to test my_private_method.
http://mathandprogramming.blogspot.com/2010/01/ruby-testing-private-methods.html
obj.send for private methods was replaced by send! in 1.9, but later send! was removed again. So obj.send works perfectly well.
In order to do this:
disrespect_privacy #object do |p|
assert p.private_method
end
You can implement this in your test_helper file:
class ActiveSupport::TestCase
def disrespect_privacy(object_or_class, &block) # access private methods in a block
raise ArgumentError, 'Block must be specified' unless block_given?
yield Disrespect.new(object_or_class)
end
class Disrespect
def initialize(object_or_class)
#object = object_or_class
end
def method_missing(method, *args)
#object.send(method, *args)
end
end
end
I know I'm late to the party, but don't test private methods....I can't think of a reason to do this. A publicly accessible method is using that private method somewhere, test the public method and the variety of scenarios that would cause that private method to be used. Something goes in, something comes out. Testing private methods is a big no-no, and it makes it much harder to refactor your code later. They are private for a reason.

Resources