I am trying to run a very basic test with Terminal and Sublime Text 3. My simple test runs, but fails (undefined local variable or method 'x')
My folder hierarchy looks like this:
spec_helper.rb looks like this:
require_relative '../test'
require 'yaml'
test_spec.rb is extremely basic
require 'spec_helper.rb'
describe "testing ruby play" do
it "finds if x is equal to 5" do
x.should eql 5
end
end
and my test.rb file has x = 5 That's it.
Will a variable only be recognizable if it's part of a class? And do I need to call a new class every time I run my test?
From the docs
require(name) → true or false
Loads the given name, returning true if successful and false if the feature is already
loaded.
[snip]
Any constants or globals within the loaded source file will be
available in the calling program’s global namespace. However, local
variables will not be propagated to the loading environment.
You could use a constant in your required file:
X = 5
...
X.should eql 5 # => passes
But you probably want to do something entirely different here. Perhaps you could expand on the question and explain what you are trying to accomplish.
Related
I want to be able to statically analyze my code. That is, to know from the plain text of the file where every function and variable comes from. IDEs and text editor plugins work better when they can trace the origin of every symbol as well.
So for example, if I have application code like this:
#...
Y.some_method()
#...
Then I want to see Y in an include/import/require/extend/def statement somewhere on the page.
In other languages I use, one can explicitly choose which sub-parts of a namespace to bring in to the current context.
Python:
from X import Y
Haskell:
import X (Y)
Elixir:
alias X.Y, as: Y
And while it's possible to import all contained names in Python, the "wildcard import" is frowned upon:
from X import *
". . . they make it unclear which names are present in the namespace, confusing both readers and many automated tools."
In Ruby, it seems that this fully implicit "wildcard" way is the only way to bring in a contained name:
include X
This makes Y available, but is there some way to make this explicit? The docs for Ruby include don't show any options.
What I'd really like to do in Ruby is something like one of these:
from X include Y
include X::Y as Y
The best I've come up with so far is:
require 'x/y' ; Y = X::Y
Here's a crazy hack in the answer to another question which would enable this.
Try this. But I agree with #tadman that you should consider doing it in the Ruby way.
Object.define_singleton_method(:include) do |*mths, from: nil|
mod = from || mths.first
mod = mod.dup
if from
all_mths = mod.instance_methods
(all_mths - mths).each { |mth| mod.send :undef_method, mth }
end
super(mod)
end
module Foobar
def foo
puts :foo
end
def bar
puts :bar
end
end
class Abc
include Foobar
end
Abc.new.foo # => 'foo'
Abc.new.bar # => 'foo'
class AbcWithoutBar
include :foo, from: Foobar
end
AbcWithoutBar.new.foo # => 'foo'
AbcWithoutBar.new.bar # => NoMethodError
Ruby always executes the code that you require
And since there is no partial execution of a file there cannot be partial require.
When you require a feature Ruby locates the corresponding file using the load paths in $: and then double checks against the list of loaded files in $" and if the file has not yet been loaded executes the file.
Ruby is a dynamic language, the best way to reason about its source code is halting a running program rather than statically. In fact even class and def are not declarations but just method calls that are executed at runtime. Consider for example this contrived example
class Surprise < [Array, Hash, Fixnum, Object].sample
end
If you want to know where a method or class has been defined best use pry. You can require pry and then use binding.pry to stop anywhere in your source code and spelunk around to inspect objects and source code. Two of the most useful commands are ls and $
ls prints all methods of an object or class
$ prints the file location and source code of a method
Coming to Ruby from a PHP background, I'm used to being able to use require, require_once, include, or include_once which all have a similar effect, but the key being they continue to process code in the same scope where the include / require command was invoked.
Example:
sub.php
<?php
echo $foo;
main.php
<?php
$foo = 1234;
include('sub.php'); // outputs '1234'
When I first started using Ruby I tried to include / require / require_relative / load other .rb files, and after becoming a little frustrated with not having it work how I would expect it to I decided that there were better ways to go about breaking up large files and that Ruby didn't need to behave in the same way PHP did.
However, occasionally I feel that for testing purposes it would be nice to to load code from another .rb file in the way PHP does - in the same scope with access to all the same variables - without having to use class / instance variables or constants. Is this possible? Maybe somehow using a proc / binding / or eval command?
Again, I'm not advocating that this should be used during development - but I am curious if it is possible - and if so, how?
Yes, this is possible, although certainly not something I'd recommend doing. This works:
includer.rb:
puts var
include.rb:
var = "Hello!"
eval(File.read("include.rb"), binding)
Running this (Ruby 2.2.1, Ruby 1.9.3) will print Hello!. It works simply: eval takes an optional binding with which to evaluate the code it is passed, and Kernel#binding returns the current binding.
To have code run in same binding, you could simply eval the file contents as follows:
example.rb
class Example
def self.called_by_include
"value for bar"
end
def foo
puts "Called foo"
end
eval( File.read( 'included.rb' ) )
end
Example.new.bar
included.rb
BAR_CONSTANT = called_by_include
def bar
puts BAR_CONSTANT
end
Running ruby example.rb produces output
value for bar
The important thing is the eval( File.read( 'included.rb' ) ) code, which if you really wanted you could define as a class method on Object, to allow arbitrary source to be included with a convenience function*. The use of constants, class variables etc just shows influences working in both directions between the two pieces of source code.
It would be bad practice to use this in any production code. Ruby gives you much better tools for meta-programming, such as ability to use mix-ins, re-open classes, define methods from blocks etc.
* Something like this
class Object
def self.include_source filename
eval( File.read( filename ) )
end
end
And the line in example.rb would become just
include_source 'included.rb'
Again I have to repeat this is not such a great idea . . .
To import external .rb file in your code, I'm not sure but I think it have to be a gem.
Use require followed by the name of the gem you want to import.
Example
require 'foobar'
# do some stuff
Or you can use load to import entire rb file
load 'foobar.rb'
# do some stuff
Good luck and sorry for my english
Yes, I know I can just use load instead of require. But that is not a good solution for my use case:
When the app boots, it requires a config file. Each environment has its own config. The config sets constants.
When the app boots, only one environment is required. However, during testing, it loads config files multiple times to make sure there are no syntax errors.
In the testing environment, the same config file may be loaded more than once. But I don't want to change the require to load because every time the a spec runs, it reloads the config. This should be done via require, because if the config has already been loaded, it raises already initialized constant warnings.
The cleanest solution I can see is to manually reset the require flag for the config file after any config spec.
Is there a way to do that in Ruby?
Edit: adding code.
When the app boots it calls the init file:
init.rb:
require "./config/environments/#{ ENV[ 'RACK_ENV' ]}.rb"
config/environments/test.rb:
APP_SETTING = :foo
config/environments/production.rb:
APP_SETTING = :bar
spec/models/config.rb: # It's not a model spec...
describe 'Config' do
specify do
load './config/environments/test.rb'
end
specify do
load './config/environments/production.rb'
end
Yes it can be done. You must know the path to the files that you want to reload. There is a special variable $LOADED_FEATURES which stores what has been loaded, and is used by require to decide whether to load a file when it is requested again.
Here I am assuming that the files you want to re-require all have the unique path /myapp/config/ in their name. But hopefully you can see that this would work for any rule about the path name you can code.
$LOADED_FEATURES.reject! { |path| path =~ /\/myapp\/config\// }
And that's it . . .
Some caveats:
require does not store or follow any kind of dependency tree, to know what it "should" have loaded. So you need to ensure the full chain of requires starting with the require command you run in the spec to re-load the config, and including everything you need to be loaded, is covered by the removed paths.
This will not unload class definitions or constants, but simply re-load the files. In fact that is literally what require does, it just calls load internally. So all the warning messages about re-defining constants will also need to be handled by un-defining the constants you expect to see defined in the files.
There is probably a design of your config and specs that avoids the need to do this.
if you really want to do this, here's one approach that doesn't leak into your test process. Fork a process for every config file you want to test, communicate the status back to the test process via IO.pipe and fail/succeed the test based on the result.
You can go as crazy as you want with the stuff you send down the pipe...
Here's some quick and dirty example to show you what I mean.
a config
# foo.rb
FOO = "from foo"
another config
# bar.rb
FOO = "from bar"
some faulty config
# witherror.rb
asdf
and your "test"
# yourtest.rb
def load_config(writer, config_file)
fork do
begin
require_relative config_file
writer.write "success: #{FOO}\n"
rescue
writer.write "fail: #{$!.message}\n"
end
writer.close
exit # maybe this is even enough to NOT make it run your other tests...
end
end
rd, writer = IO.pipe
load_config(writer, "foo.rb")
load_config(writer, "bar.rb")
load_config(writer, "witherror.rb")
writer.close
puts rd.read
puts rd.read
puts rd.read
puts FOO
The output is:
success: from foo
success: from bar
fail: undefined local variable or method `asdf' for main:Object
yourtest.rb:24:in `<main>': uninitialized constant FOO (NameError)
as you can see, the FOO constant doesn't leak into your test process etc.
Of course you're only through half way because there's more to it like, making sure only one process runs the test etc.
Frankly, I don't think this is a good idea, no matter what approach you chose because you'll open a can of worms and imho there's no really clean way to do this.
I'm trying to write a custom tool that runs ruby unit tests with my customizations.
What I need it to do is to load a certain TestCase from given file(through require or whatever), and then run it after doing some calculations and initializations.
Problem is, the moment I require "test/unit" and a test case, it runs immediately.
What can I do with this?
Thanks.
Since you're running 1.9 and test/unit in 1.9 is merely a wrapper for MiniTest, the following approach should work:
implement your own custom Runner
set MiniTest's runner to your custom runner
Something like (shameless plug from EndOfLine Custom Test Runner, adjusted to Ruby 1.9):
fastfailrunner.rb:
require 'test/unit'
class FastFailRunner19 < MiniTest::Unit
def _run args = []
puts "fast fail runner"
end
end
~
example_test.rb:
require 'test/unit'
class ExampleTest < Test::Unit::TestCase
def test_assert_equal
assert_equal 1, 1
end
def test_lies
assert false
end
def test_exceptions
raise Exception, 'Beware the Jubjub bird, and shun the frumious Bandersnatch!'
end
def test_truth
assert true
end
end
run.rb:
require_relative 'fast_fail_runner'
require_relative 'example_test'
MiniTest::Unit.runner= FastFailRunner19.new
If you run this with
ruby run.rb
the custom FastFailRunner19 will be used, which does nothing.
What about reading file content as a regular text file and doing eval on its content after you initialize/calculate things you say? It may not be sufficient for your needs and may require manual setup and execution of testing framework.
Like that (I put heredoc instead of reading file). Basically content is just a string containing your test case code.
content = <<TEST_CASE
class YourTestCase
def hello
puts 'Hello from eval'
end
end
YourTestCase.new.hello
TEST_CASE
eval content
Note: Altough I'd rather not use eval if there is another way. One should be extra careful when evaling code from string manually in any language.
You could collect the test cases you want to deferred its executions and store them in an array. Afterwards you would create a block execution code. For instance:
test_files = ['test/unit/first_test.rb'] #=> Testcases you want to run
test_block = Proc.new {spec_files.each {|f|load f} } #=> block storing the actual execution of those tests.
Once you're ready to call those testcases you just do test_block.call.
To generalize a bit, when thinking about deferring or delaying code executions, closures are a very elegant and flexible alternative.
I'd like to test class and gem loading. Have a look at the following stupid test case:
require 'rubygems'
require 'shoulda'
class SimpleTest < Test::Unit::TestCase
context 'This test' do
should 'first load something' do
require 'bundler'
assert Object.const_defined? :Bundler
end
should 'second have it reset again' do
assert !Object.const_defined?(:Bundler)
end
teardown do
# This works, but is tedious and unclean
#Object.send :remove_const, :Bundler rescue nil
# Instead I want something like this ;)
magic_reset
end
end
end
How about creating a subclass of Test::Unit::TestCase which runs the test method in a forked process?
class ForkingTestCase < Test::Unit::TestCase
def run(...)
fork do
super.run(...)
# somehow communicate the result back to the parent process
# this is the hard part
end
end
end
If this can be implemented, it should then just be a matter of changing the base class of your test case.
AFAIK, you cannot unload a file that you have loaded. You need to start a separate Ruby process for every test. (Or a separate Ruby instance if you are running on a Ruby implementation which supports multiple instances in the same process.)
Try using Kernel#load with wrap set to true:
load(filename, wrap=false) → true
Loads and executes the Ruby program in the file filename. If the
filename does not resolve to an absolute path, the file is searched
for in the library directories listed in $:. If the optional wrap
parameter is true, the loaded script will be executed under an
anonymous module, protecting the calling program’s global namespace.
In no circumstance will any local variables in the loaded file be
propagated to the loading environment.
each time you want to do a test of bundler, load it into a new anonymous module, do your tests on the bundler class within that module, and then go onto your next test.
If your code refers to the Bundler constant, then you'd have to set and unset that constant, though.
I haven't tried it myself, but can't see why it wouldn't work.
Try keeping track of what constants were defined before your tests started, and what constants are defined after your test finished, and remove the constants that were defined during the test.
I guess this isn't so much telling you how to call magic_reset as how to implement magic_reset.