isolated tests, stub_const and modules - ruby

I have a class that I want to spec. The class includes a module that I do not want to load. Application code goes something like this:
class Foo
include Bar
...
end
In my isolated spec, I only require foo.rb, but upon doing that, Ruby attempts to include the Bar module constant, which at this point has not been defined.
Rspec offers a method called stub_const, but it appears to be intended for use inside examples, so the constant can be undefined again upon the clean up after the example.
This will obviously not work if stub_const is called outside of an example.
What is the optimal way to solve this problem? The solution should ideally
allow Rspec to load my class
define a fake module of my choosing
run examples which instantiate the Foo class that now includes the fake module
undefine the fake module to return the original state

What about defining an empty (or stubbed) Bar module beforehand?
With foo.rb:
class Foo
include Bar
def to_s
"Foo"
end
end
This foo_test.rb does works:
module Bar
end
require 'foo'
f = Foo.new
The empty module needs to appear before the require of foo, but you could set it up in a specific file in your test suite (especially if you have to reuse it).

Related

Ruby: require vs include/extend: Using a module inside another, but not exposing to users

I was having trouble understanding an error while unit testing my module, which is a mixin.
Suppose the mixin to be tested is module A:
require 'path/b'
module A
def methodA()
puts methodB.attr1
end
end
And it depends on another mixin B which was defined in a file at path/b.rb
module B
def methodB
return someObject #that has property 'attr1'
end
end
Now, we have a class to unit test module A
require 'path/moduleA'
class TestA
include Path::moduleA
end
describe 'test moduleA.methodA'
it 'does something'
testObject = TestA.new
testObject.methodA()
expect(....)
end
end
I get following error on running the rspec test
NameError:
undefined local variable or method `methodB' for #<TestA:0x00007f77a03db9a8>
I am able to resolve it by one of following ways:
including module B in module A
including module B in class TestA
Questions
I am not clear why include is required to get access to methodB in
module A and class TestA when 'require' was already added in module A.
My intention is to use methods of module B in module A, but not let users of module A access module B methods automatically.
resolution 1 above gives users of A, access to B's methods
resolution 2 forces users of A (user -> the unit test class in this example) to include A's dependency B directly, even though user is only interested in accessing A's methods.
Hence, both resolutions don't achieve what I want. Is there a way to achieve it?
I'm new to Ruby so may be it doesn't support this. I'm from Java background where I would model A and B as two classes, make an instance of B as field of A, then expose A's own public methods to users of A. But since they are mixins, I need to use modules in ruby.
Just to be very explicit: require / require_relative / load and include / extend / prepend have absolutely nothing whatsoever to do with each other.
The former three simply run a Ruby file. That's it. That is all they do. They differ in how and where they search for the file, but after they found the file, they don't do anything else than just execute it.
The latter three add a module to the ancestor chain. include essentially makes the module the superclass, extend is really the same as singleton_class.include (i.e. makes the module the superclass of the singleton class), and prepend inserts the module at the beginning of the ancestor's chain, i.e. actually before the class that it is prepended to.
require just tells ruby to read / load the code inside the ruby file. In this case it will just define the module. However in order for code inside a module to be included inside another module or class, you must include it inside the module or class. So you should just as you mentioned do:
require 'path/b'
module A
include B
def methodA()
puts methodB.attr1
end
end
You should not need to change your test with this since module A already includes module B. However this is not a very good OOP design pattern here. But hopefully you understand why now.
After more googling, I found the answer to my 2nd question using suggestion from:
https://makandracards.com/makandra/977-calling-selected-methods-of-a-module-from-another-module
so basically i did:
require 'path/b'
module A
module B_RequiredMethods
include Path::B
module_function :methodB
end
def methodA
puts B_RequiredMethods.methodB.attr1
end
end
In my case, B_RequiredMethods could be named properly to represent the method of B which would be exposed by it. Ideally, I would make those methods class level methods of B, but it is managed by some other team.

How many ways to include or inherit module inside a class

I am trying to understand concepts of including or inheriting a module inside a class. Whenever I read i get new ways to include or inherit a module inside a class. So just want to know how many ways are there. Here are the examples I am sharing:
Example 1
module TimeExtensions
refine Fixnum do
def minutes; self * 60; end
end
end
class MyApp
using TimeExtensions
def initialize
p 2.minutes
end
end
Example 2
VAL = 'Global'
module Foo
VAL = 'Foo Local'
class Bar
def value1
VAL
end
end
end
class Foo::Bar
def value2
VAL
end
end
Example 3
module Foo
def foo
puts 'heyyyyoooo!'
end
end
class Bar
include Foo
end
Out of the three, only the last one includes a module into the class. That is, it adds the methods from the module to the class.
The first one is called refinement. If instead of refining Fixnum you just added a #minutes method to it, Fixnum will have that method for everyone that uses it, as long as your code is loaded. This might cause conflicts with other implementations of the same method (for example active record's #minutes) and leads to obscure, hard to debug issues. Instead, you can refine the Fixnum class and it will have that method only in the scope where you added the refinement with using.
It has some issues and it's not very widely used. But for the purposes of this question, the differences between including a module and using a refinement are:
including will add methods to the class where you do the inclusion, while refinements make other classes behave differently in the context where they are used
including is semantical (all objects will have the new included methods), while refinements are lexical (after you close the class, if you reopen it again, you won't see the refined methods)
The second one is scoping the class inside the module. This doesn't add methods anywhere. It is just due to the way constants are searched that if the constant (in this case VAL) isn't found in the current class, it is searched further up the tree. It's not that simple actually, if you want more details, you can read here.

What are the advantages of using `require` inside a module instead of at the top?

Usually, I put most of my require statements at the top of a file. While reading the source code for Poltergeist, I noticed the following
module Capybara
module Poltergeist
require 'capybara/poltergeist/utility'
require 'capybara/poltergeist/driver'
require 'capybara/poltergeist/browser'
# more requires
end
end
Actual source
What are the advantages of using require this way?
The advantage in this case is that the Capybara::Poltergeist module exists before those modules are required. Since those modules all extend the Capybara::Poltergeist module, this is just a way to ensure that they aren't loaded before the module is actually available. Placing the require statements after the module definition would have the same effect.
Consider the following:
# foobar.rb
require './bar_module'
module Foo
module Bar
end
end
# bar_module.rb
module Foo::Bar
def baz
"hi!"
end
end
This setup will fail because the non-nested Foo::Bar syntax will expect Foo to already exist by the time this module is called. By changing the first file to:
module Foo
module Bar
require './bar_module'
end
end
The require will work, since Foo::Bar will exist by the time that bar_module starts doing its thing.
In this particular instance, it doesn't have much practical effect, since Poltergeist uses the nested module syntax (module Foo; module Bar) rather than the collapsed syntax (module Foo::Bar), but it's a good practice that basically delineates "these requires expect this module to exist".
I don't know what's the advantage in your example.
I sometimes use require inside a method definition.
I do this for methods which are used rarely, but need large libraries. The advantage: The large library is only loaded, when it is really needed.
require checks, if the library is already loaded. So I have no problem with double loading a library.

How do you make hierarchical class names in Ruby?

In Ruby a class named Foo would be defined with class Foo, used via require 'foo' and would live in $:[0]/foo.rb or something like that.
But what about Foo::Bar? Would it be called with require 'foo/bar'? Would it live in $:[0]/foo/bar.rb? And how would it be defined?
I am very used to Perl, where for personal projects I would make nested classes like Project::User, Project::Text::Index, Project::Text::Search, etc. I would then make a file like Project/Text/Index.pm, which would start with package Project::Text::Index, and be called via use Project::Text::Index;.
Now I'm starting a project in Ruby and have no idea how to do this. For some reason none of the Ruby books or docs I've read mention perl-style hierarchical class naming. When they mention inheritance it's usually via a trivial made up example like class Foo < Bar which doesn't really help me. Yet I figure it must be possible to do what I'm attempting because Rails (just to take one example) has classes like ActionView::Helpers::ActiveModelFormBuilder.
You're combining a couple of concepts here which aren't really related, namely the load path, inheritance, and the scope resolution operator.
When requiring (or loading) files the argument to the require keyword is simply taken as a file path and appended to the load search path (the .rb extension is optional for require). Inheritance and nesting don't come into play here and any file can define anything it wants, e.g.:
require 'foo' # Looks for "foo.rb" in each of $:
require 'foo/bar' # Looks for "foo/bar.rb" in each of $:
Nested classes (and modules, variables, etc) are defined as expected but resolved with the scope resolution operator, e.g.:
class Foo
def foo; 'foo'; end
class Bar
def bar; 'bar'; end
end
end
Foo.new.foo # => "foo"
Foo::Bar.new.bar # => "bar"
Note that class nesting and inheritance are irrelevant to the location of the file from which they are loaded. There don't seem to be any explicit conventions for class/module structuring, so you're free to do what works for you. The Ruby Language page of the Programming Ruby book might be helpful too.
You need modules. If you want a class identified by Funthing::Text::Index in Funthing/Text/Index.rb and be required with require 'Funthing/Text/Index', you do this:
# file Funthing/Text/Index.rb
module Funthing
module Text
class Index
def do_the_twist()
puts "Let's twist again!"
end
end
end
end
Then use it like this:
require "Funthing/Text/Index"
c = Funthing::Text::Index.new
Note: the file hierarchy is not required, but (in my opinion) is best practice.

Test modules with Test::Unit

I encountered a problem when trying to test a module with Test::Unit. What I used to do is this:
my_module.rb:
class MyModule
def my_func
5 # return some value
end
end
test_my_module.rb:
require 'test/unit'
require 'my_module'
class TestMyModule < Unit::Test::TestCase
include MyModule
def test_my_func
assert_equal(5, my_func) # test the output value given the input params
end
end
Now the problem is, if my_module declares an initialize method, it gets included in the test class and this causes a bunch of problems since Test::Unit seems to override/generate an initialize method. So I'm wondering what is the best way to test a module?
I'm also wondering wether my module should become a class at this point since the initialize method is made for initializing the state of something. Opinions?
Thanks in advance !
Including an initialize method in a module feels very wrong to me, so I'd rethink that at the very least.
To answer your question about testing this as a module more directly, though, I would create a new, empty class, include your module in it, create an instance of that class, and then test against that instance:
class TestClass
include MyModule
end
class TestMyModule < Unit::Test::TestCase
def setup
#instance = TestClass.new
end
def test_my_func
assert_equal(5, #instance.my_func) # test the output value given the input params
end
end
Yeah, your initialize should definitely suggest that you're going towards a class. A module in ruby often feels like an interface in other languages, as long as you implement some basic things when you include the module you'll get a lot for free.
Enumerable is a great example, as long as you define [] and each when you include Enumerable you suddenly get pop, push, etc.
So my gut feeling about testing modules is that you should probably be testing classes that include the module rather than testing the module itself unless the module is designed to not be included in anything, it's simply a code storage mechanism.

Resources