can I have RSpec without a class for methods - ruby

I'm just writing some algorithm methods that I don't want to put in a class. So I just put them in my ruby file.
But I cant figure out how to write test or more specifically use describe :xxx since I dont' have a class name to put after the main describe. Any ideas?

You can put any string after the describe statement:
describe "Something You Want To Test" do
# Your specs here
end

Related

What if objects can print themselves in Ruby? [Object#print]

I was thinking wouldn't it be cool to have a print method defined in the Ruby Object class? Consider the following:
class Object
def print
puts self.to_s
end
end
23.times &:print
Is there any issue in having something like this? Seems like a good feature to have. It also appears easy to read.
There's already Object#inspect defined. Plus, there's already Kernel#print defined as private method in Object class and every class that inherits from it.
This method already exists in the Ruby standard library. However, it has a different name: display.
23.times &:display
# 012345678910111213141516171819202122
As you can see, it does not write a newline after the object's string representation; it is ill-suited for object inspection.
The main issue with adding methods to Object is that they become universal and may clash with similarly named methods in other libraries or in your project.
There are already multiple simple ways to output data or convert to string form in Ruby core, so the risk of a clash (on a very useful method name) likely outweighs any benefits from nicer syntax even in your own code.
If you have a smaller set of classes in your own project, where you feel this would be a useful feature to have, then this is an ideal use case for mix-ins.
Define a module:
module CanPrintSelf
def print
puts self.to_s
end
end
And include it in any class you want to have the feature:
class MyClass
include CanPrintSelf
end
my_object = MyClass.new
my_object.print
So you can have this feature if you like it, and you don't need to modify Object.

How to check whether a variable is an instance of a module's subclass using rspec?

I have a class structure that looks like this:
module MyModule
class MyOuterClass
class MyInnerClass
end
end
end
I'm trying to make sure that a variable was correctly instantiated as a MyInnerClass using Rspec. printing the type of the class, it was MyModule::MyOuterClass::MyInnerClass. However, if I try to run the line
expect{#instance_of_MyInnerClass}.to be_an_instance_of(MyModule::MyOuterClass::MyInnerClass)
I get the error "You must pass an argument rather than a block to use the provided matcher." Additionally, the classes are in another location, so I can't just check
[...] be_an_instance_of(MyInnerClass)
Rspec complains that MyInnerClass is an uninitialized constant. So, I would like to ask how to verify that a variable is an instance of MyInnerClass using RSpec.
Don't Pass a Block
Rspec 3.x uses an expect method rather than a block syntax (see RSpec 3 Expectations 3.0). To get your spec to pass, and clean it up, you can use the following:
module MyModule
class MyOuterClass
class MyInnerClass
end
end
end
describe MyModule::MyOuterClass::MyInnerClass do
it "is correctly instantiated" do
expect(subject).to be_an_instance_of MyModule::MyOuterClass::MyInnerClass
end
end
Note the use of the implicit subject, passed as an argument to #expect. You can certainly pass other local or instance variables instead, but in this case subject is already defined for you as MyModule::MyOuterClass::MyInnerClass.new.
Most of us are using the preferred Rspec syntax, so it would be:
expect(#instance_of_MyInnerClass).to be_a MyInnerClass

How to pass an external object to rspec tests?

I have a series of RSpec tests - each one living in their own files - that all need a specific object to be ran (shop).
Since setting up the shop object is complicated, I want to have a master file that builds the shop object and then passes it to all the RSpec test files in turn. The idea is that those writing the test scripts do not need to know anything about the setup step.
I could not figure out how to bring something from the outside world into an rspec test case. I have tried variations around the lines of this:
file master.rb:
describe "Testing tests/some_test.rb" do
before :all do
#shop = some_complex_operations
end
load 'tests/some_test.rb'
end
file tests/some_test.rb:
describe 'Some feature' do
it 'should know about #shop'
expect(#shop).not_to be nil
end
end
When I run rspec master.rb then some_test.rb fails.
I know what I outlined is probably a very wrong way to go about this, but I hope at least you get the idea.
How can I implement such a thing cleanly?
What you need is a global hook.
You can create a spec_helper and add there a before all global hook. For example:
RSpec.configure do |config|
config.before(:each) do
#shop = "in case you need a fresh instance of shop each time"
end
config.before(:all) do
#shop = "in case you need a single instance (i.e. you don't modify it)"
end
end
Then you should require this helper from each of your specs. The spec_helper can store any global hooks as well as helpers, or other dependencies for your tests.
When you are writing a test, it should test one thing. shop might be a very complex object, which many objects and methods interact with, but I should guess none of them are aware of the complexity of shop - each method is interested in some aspect of shop.
Therefore I suggest that instead of building a complex object for each test, make a double of it, make it expect what's relevant, and then simply stub the rest:
describe 'Some feature' do
let(:shop) { double('shop').as_nil_object }
let(:my_object) { MyClass.new(shop: shop) }
it 'should do something awesome with shop' do
expect(shop).to receive(:awesome_data).and_return(my_data: 'is_this')
expect(my_object.do_something_awesome).to eq 'how awesome is_this?'
end
end

Multiple classes of the same name in Ruby

I have an existing codebase of unit tests where the same classes are defined for each test, and a program that iterates over them. Something like this:
test_files.each do |tf|
load "tests/#{tf}/"+tf
test= ::Kernel.const_get("my_class")
Test::Unit::UI::Console::TestRunner.run( test )
While working on these tests, I've realized that ruby allows you to require classes with the same name from different files, and it merges the methods of the two. This leads to problems as soon as the class hierarchy is not the same: superclass mismatch for class ...
Unfortunately, simply unloading the class is not enough, as there are many other classes in the hierarchy that remain loaded.
Is there a way to execute each test in a different global namespace?
While I figure that using modules would be the way to go, I'm not thrilled with idea of changing the hundreds of existing files by hand.
--EDIT--
Following Cary Swoveland's suggestion, I've moved the test running code to a separate .rb file and am running it using backticks. While this seems to work well, loading the ruby interpreter and requireing all the libraries again has a considerable overhead. Also, cross-platform compatibility requires some extra work.
first I wanted to propose the following solution:
#main.rb
modules = []
Dir.foreach('include') do |file|
if file =~ /\w/
new_module = Module.new
load "include/#{file}"
new_module.const_set("StandardClass", StandardClass)
Object.send(:remove_const, :StandardClass)
modules << new_module
end
end
modules.each do |my_module|
my_module::StandardClass.standard_method
end
#include/first.rb
class StandardClass
def self.standard_method
puts "first method"
end
end
#include/second.rb
class StandardClass
def self.standard_method
puts "second method"
end
end
this wraps each class with it's own module and calls the class inside it's module:
$ruby main.rb
second method
first method
But as you answered there are references to other classes so modules can break them.
I checked this by calling the third class from one of the wrapped classes and got:
include/second.rb:5:in `standard_method': uninitialized constant StandardClass::Independent (NameError)
so probably using backticks is better solution but I hope my answer will be helpful for some other similar situations.

Trouble accessing method from module?

I'm including someone else's module, which seems to be split across multiple files. It looks something like this:
alfred.rb:
require 'alfred/ui'
module Alfred
<code>
end
alfred/ui.rb:
module Alfred
class Logger
def info(msg)
logger.info msg
end
end
end
I'm trying to access the info method from my code, which has require "alfred" at the top, using this syntax:
Alfred::Logger.info("my message")
which is not working, and results in #<NameError: uninitialized constant Alfred::Logger>.
Secondly, I would love if someone could explain how exactly Ruby "constructs" a module that is split across several files like this. Does the code from alfred/ui.rb get included at the end of alfred.rb at the beginning where the require line is? Does it all get put together in some other fashion? Maybe it doesn't even matter, but it seems it would help me wrap my head around it better.
The full code of the module I'm using is at Github in case anyone is interested.
Can someone help explain why I can't use the method this way, and what might be done to fix it?
#info is a instance method, meaning it only exists on instances of the Logger class. If you want to call it directly from your code, you'd call Alfred::Logger.new.info "some message", but that seems a bit odd. Usually that type of thing is used internally by the library.
If it had been defined like a class method:
module Alfred
class Logger
def self.info(msg)
logger.info msg
end
end
end
then you would be able to call it as you're attempting.

Resources