What I want is a way of not having to 'require' the class under test in each spec file.
So hoping there is a means of setting the root of the source code under test and rspec automatically mapping my tests, or any other means of automatically mapping specs to ruby files.
In Rspec for rails this happens magically, but this is not a rails project and I can't find any useful information.
I am assuming you have a lib folder and a spec folder within your project where you have code and specs respectively. Create a spec/spec_helper.rb and add
# project_name/spec/spec_helper.rb
$: << File.join(File.dirname(__FILE__), "/../lib")
require 'spec'
require 'main_file_within_lib_folder_that_requires_other_files'
Now within your individual spec files now you just need to add the following line like rails
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
What you have to do is to redefine Object.const_missing.
Found this basic example, modify it to fit your needs (set the right path, etc.):
def Object.const_missing(name)
#looked_for ||= {}
str_name = name.to_s
raise "Class not found: #{name}" if #looked_for[str_name]
#looked_for[str_name] = 1
file = str_name.downcase
require file
klass = const_get(name)
return klass if klass
raise "Class not found: #{name}"
end
Related
I have a file /project/lib/invaccessor.rb with the following content
class InvAccessor
def initialize
#browser = "browser"
end
end
and a spec file project/spec/invaccessor_spec.rb which requires it
require_relative '../lib/invaccessor'
describe Invaccessor do
it {expect(2).to be_even}
end
When I run rspec spec/invaccessor.rb I get an uninitialized constant error for Invaccessor. Do I have to put all file contents in a module in order to access them?
I'm using Ruby 2.2.2.
Yes, you can.
Try this inside the directory where your classfile.rb lies:
>> require './classfile'
=> true
>> A
=> A
You definitely don't have to put a class into a module to require it.
Following the example here:
http://rspec.info
however it fails with:
kernel_require.rb:45:in `require': cannot load such file -- bowling.rb (LoadError)
even though I've got a bowling.rb file.
Any suggestions?
UPDATE
Project listing:
ls -l
-rw-r--r-- 1 snowcrash snowcrash 77 10 Jul 19:43 bowling.rb
-rw-r--r-- 1 snowcrash snowcrash 205 10 Jul 19:49 bowling_spec.rb
$ rspec bowling_spec.rb
/Users/snowcrash/.rvm/rubies/ruby-2.0.0-p195/lib/ruby/site_ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require': cannot load such file -- bowling (LoadError)
and code listings:
Spec:
# bowling_spec.rb
require 'bowling'
describe Bowling, "#score" do
it "returns 0 for all gutter game" do
bowling = Bowling.new
20.times { bowling.hit(0) }
bowling.score.should eq(0)
end
end
Class file:
# bowling.rb
class Bowling
def hit(pins)
end
def score
0
end
end
The rspec home page unfortunately does not tell you about initializing rspec in your project.
Assuming you have a project folder called 'bowling', inside the bowling folder run
rspec --init
This will create the spec directory and two files
spec/spec_helper.rb
.rspec
The .rspec file lets you define preferences like color and format
--color
--format documentation
Now in spec_helper.rb, add require "bowling"
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# Require this file using `require "spec_helper"` to ensure that it is only
# loaded once.
require "bowling"
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.run_all_when_everything_filtered = true
config.filter_run :focus
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = 'random'
end
Now inside your bowling_spec.rb, add `require "spec_helper"
require "spec_helper"
class Bowling
def hit(pins)
end
def score
0
end
end
Also, any other specs you add you need to add require "spec_helper". The comments in spec_helper.rb explain why this is necessary.
Here is a good beginner explanation of setting up and working with rspec
Good luck
I'm a complete noob in ruby (coming from java) and had a similar problem to get rspec running.
The above answer helped to point the right way but did not work for me at first.
After reading the provided link i got it running with the following solution:
first run rspec --init.
then i edited the spec_helper.rb file with require_relative '../bowling'.
looked like this:
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# The generated `.rspec` file contains `--require spec_helper` which will cause this
# file to always be loaded, without a need to explicitly require it in any files.
#
# Given that it is always loaded, you are encouraged to keep this file as
# light-weight as possible. Requiring heavyweight dependencies from this file
# will add to the boot time of your test suite on EVERY test run, even for an
# individual file that may not need all of that loaded. Instead, consider making
# a separate helper file that requires the additional dependencies and performs
# the additional setup, and require it from the spec files that actually need it.
#
# The `.rspec` file also contains a few flags that are not defaults but that
# users commonly want.
#
require_relative '../bowling'
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
The other two files looked like this:
bowling_spec.rb:
# bowling_spec.rb
describe Bowling, "#score" do
it "returns 0 for all gutter game" do
bowling = Bowling.new
20.times { bowling.hit(0) }
bowling.score.should eq(0)
end
end
and bowling.rb:
# bowling.rb
class Bowling
def hit(pins)
end
def score
-1
end
end
That was enough. I found it strange that there is no hint in the docs to run rspec --init first to get it running.
Maybe something is wrong with my installation (rbenv ) so that i have to use require_relative ?
In my Rails application I have a file sample_data.rb inside /lib/tasks as well as a bunch of test files inside my /spec directory.
All these files often share common functionality such as:
def random_address
[Faker::Address.street_address, Faker::Address.city].join("\n")
end
Where should I put those helper functions? Is there some sort of convention on this?
Thanks for any help!
You could create a static class, with static functions. That would look something like this:
class HelperFunctions
def self.random_address
[Faker::Address.street_address, Faker::Address.city].join("\n")
end
def self.otherFunction
end
end
Then, all you would need to do is:
include your helper class in the file you want to use
execute it like:
HelperFunctions::random_address(anyParametersYouMightHave)
When doing this, make sure you include any dependencies in your HelperFunctions class.
If you're sure it's rake only specific, you also can add in directly in RAILS_ROOT/Rakefile (that's probably not the case for the example you use).
I use this to simplify rake's invoke syntax :
#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__)
def invoke( task_name )
Rake::Task[ task_name ].invoke
end
MyApp::Application.load_tasks
That way, I can use invoke "my_namespace:my_task" in rake tasks instead of Rake::Task[ "my_namespace:my_task" ].invoke.
You share methods in a module, and you place such a module inside the lib folder.
Something like lib/fake_data.rb containing
module FakeData
def random_address
[Faker::Address.street_address, Faker::Address.city].join("\n")
end
module_function
end
and inside your rake task just require the module, and call FakeData.random_address.
But, if it is like a seed you need to do every time you run your tests, you should consider adding this to your general before all.
E.g. my spec_helper looks like this:
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
RSpec.configure do |config|
config.use_transactional_fixtures = true
config.infer_base_class_for_anonymous_controllers = false
config.order = "random"
include SetupSupport
config.before(:all) do
load_db_seed
end
end
and the module SetupSupport is defined in spec/support/setup_support.rb and looks as follows:
module SetupSupport
def load_db_seed
load(File.join(Rails.root, 'db', 'seeds.rb'))
end
end
Not sure if you need to load the seeds, or are already doing this, but this is the ideal spot to also generate needed fake data.
Note that my setup support class is defined in spec/support because the code is only relevant to my specs, I have no rake task also needing the same code.
I have to Ruby files: one contains a module with some methods for statistical calculation, in the other file I want to call one of the methods in the module.
How can I do that in Ruby?
Is that the right way?
require 'name of the file with the module'
a=[1,2,3,4]
a.method1
Require needs the absolute path to the file unless the file is located in one of Ruby's load paths. You can view the default load paths with puts $:. It is common to do one of the following to load a file:
Add the main file's directory to the load path and then use relative paths with require:
$: << File.dirname(__FILE__)
require "my_module"
Ruby 1.8 code that only loads a single file will often contain a one-liner like:
require File.expand_path("../my_module", __FILE__)
Ruby 1.9 added require_relative:
require_relative "my_module"
In the module you will need to define the methods as class methods, or use Module#module_function:
module MyModule
def self.method1 ary
...
end
def method2
...
end
module_function :method2
end
a = [1,2,3,4]
MyModule.method1(a)
Your way is correct if your module file is in the require search path.
If your module provide methods to be used by the object itself, you must do:
require 'name of the file with the module'
a=[1,2,3,4]
a.extend MyModule # here "a" can use the methods of MyModule
a.method1
See Object#extend.
Otherwise, if you'll use the methods directly by the module, you'll use:
MyModule.method1(a)
Here I have two files:
file.rb
def method
puts "This won't be outputted."
end
puts "This will be outputted."
main.rb
require "./file"
When running main.rb it will load all the code inside file.rb so I will get "This will be outputted." on the screen.
Is it possible to load a file without having it to run the code?
Cause I want to load all the methods (in modules and classes too) without having to execute code outside these scopes.
Is it possible to load a file without having it to run the code?
No, everything in a ruby file is executable code, including class and method definitions (you can see this when you try to define a method inside an if-statement for example, which works just fine). So if you wouldn't execute anything in the file, nothing would be defined.
You can however tell ruby that certain code shall only execute if the file is run directly - not if it is required. For this simply put the code in question inside an if __FILE__ == $0 block. So for your example, this would work:
file.rb
def method
puts "This won't be outputted."
end
if __FILE__ == $0
puts "This will not be outputted."
end
main.rb
require "./file"
the if __FILE__ == $0 is nice, but a way more in keeping with ruby's Object Oriented approach is to put all the methods you want access to in a class (as class methods), and then call them from main.rb.
e.g.
file.rb
class MyUtils
def self.method
puts "this won't be outputted"
end
end
and then in main.rb
require "/.file.rb"
and when you want to use your utility methods:
MyUtils.method
I don't think modifying file is good idea - there are could be a lot of files like this one or these files belong to customer and a ton of another reasons.
Ruby is good at metaprogramming so why don't use this feature?
It could be like this.
Create file with fake module and put here the file.
File.open("mfile.rb","w") do |f|
f.write "module FakeModule
"
f.write File.open("file.rb").read
f.write "
end"
end
Then load this file:
require "/.mfile.rb
and accessing to the method:
FakeModule::method